Compare commits

...
Author SHA1 Message Date
AllinenandGinger d50e0f53d2 style: fixed EOF 2026-06-03 22:31:34 -04:00
AllinenandGinger 57e82f1783 fix: simplified comparison logic 2026-06-03 22:31:34 -04:00
AllinenandGinger 1d311073b9 feat: token expiration improvement 2026-06-03 22:31:34 -04:00
3 changed files with 43 additions and 14 deletions
+14 -8
View File
@@ -6,18 +6,24 @@
#[admin_command] #[admin_command]
pub(super) async fn issue_token(&self, expires: super::TokenExpires) -> Result { pub(super) async fn issue_token(&self, expires: super::TokenExpires) -> Result {
let expires = { let expires = {
if expires.immortal { let max_uses = expires.max_uses;
None let max_age = expires
} else if let Some(max_uses) = expires.max_uses {
Some(TokenExpires::AfterUses(max_uses))
} else if expires.once {
Some(TokenExpires::AfterUses(1))
} else if let Some(max_age) = expires
.max_age .max_age
.as_deref() .as_deref()
.map(|max_age| utils::time::timepoint_from_now(utils::time::parse_duration(max_age)?)) .map(|max_age| utils::time::timepoint_from_now(utils::time::parse_duration(max_age)?))
.transpose()? .transpose()?;
if expires.immortal {
None
} else if let Some(max_uses) = max_uses
&& let Some(max_age) = max_age
{ {
Some(TokenExpires::AfterTimeOrUses { max_uses, max_age })
} else if let Some(max_uses) = max_uses {
Some(TokenExpires::AfterUses(max_uses))
} else if expires.once {
Some(TokenExpires::AfterUses(1))
} else if let Some(max_age) = max_age {
Some(TokenExpires::AfterTime(max_age)) Some(TokenExpires::AfterTime(max_age))
} else { } else {
unreachable!(); unreachable!();
+5 -5
View File
@@ -29,23 +29,23 @@ pub enum TokenCommand {
} }
#[derive(Debug, Args)] #[derive(Debug, Args)]
#[group(required = true, multiple = false)] #[group(required = true, multiple = true)]
pub struct TokenExpires { pub struct TokenExpires {
/// The maximum number of times this token is allowed to be used before it /// The maximum number of times this token is allowed to be used before it
/// expires. /// expires.
#[arg(long)] #[arg(long, conflicts_with_all = ["immortal", "once"])]
max_uses: Option<u64>, max_uses: Option<u64>,
/// The maximum age of this token (e.g. 30s, 5m, 7d). It will expire after /// The maximum age of this token (e.g. 30s, 5m, 7d). It will expire after
/// this much time has passed. /// this much time has passed.
#[arg(long)] #[arg(long, conflicts_with_all = ["immortal", "once"])]
max_age: Option<String>, max_age: Option<String>,
/// This token will never expire. /// This token will never expire.
#[arg(long)] #[arg(long, conflicts_with_all = ["once", "max_uses", "max_age"])]
immortal: bool, immortal: bool,
/// A shortcut for `--max-uses 1`. /// A shortcut for `--max-uses 1`.
#[arg(long)] #[arg(long, conflicts_with_all = ["immortal", "max_uses", "max_age"])]
once: bool, once: bool,
} }
+24 -1
View File
@@ -41,6 +41,8 @@ pub fn is_valid(&self) -> bool {
expiry_time >= now expiry_time >= now
}, },
| Some(TokenExpires::AfterTimeOrUses { max_uses, max_age }) =>
self.uses < max_uses && max_age >= SystemTime::now(),
| None => true, | None => true,
} }
} }
@@ -63,12 +65,16 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
pub enum TokenExpires { pub enum TokenExpires {
AfterUses(u64), AfterUses(u64),
AfterTime(SystemTime), AfterTime(SystemTime),
AfterTimeOrUses {
max_uses: u64,
max_age: SystemTime,
},
} }
impl std::fmt::Display for TokenExpires { impl std::fmt::Display for TokenExpires {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
| Self::AfterUses(max_uses) => write!(f, "Expires after {max_uses} uses"), | Self::AfterUses(max_uses) => write!(f, "Expires after {max_uses} use(s)"),
| Self::AfterTime(max_age) => { | Self::AfterTime(max_age) => {
let now = SystemTime::now(); let now = SystemTime::now();
let formatted_expiry = utils::time::format(*max_age, "%+"); let formatted_expiry = utils::time::format(*max_age, "%+");
@@ -82,6 +88,23 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
| Err(_) => write!(f, "Expired at {formatted_expiry}"), | Err(_) => write!(f, "Expired at {formatted_expiry}"),
} }
}, },
| Self::AfterTimeOrUses { max_uses, max_age } => {
if *max_uses == 0 {
return write!(f, "Already expired (uses exhausted)");
}
let now = SystemTime::now();
let formatted_expiry = utils::time::format(*max_age, "%+");
match max_age.duration_since(now) {
| Ok(duration) => write!(
f,
"Expires after {max_uses} use(s) or in {} ({formatted_expiry})",
utils::time::pretty(duration)
),
| Err(_) => write!(f, "Expired at {formatted_expiry}"),
}
},
} }
} }
} }