mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-05-26 20:49:55 +00:00
130 lines
3.4 KiB
Rust
130 lines
3.4 KiB
Rust
use std::{sync::Arc, time::SystemTime};
|
|
|
|
use conduwuit::utils::{
|
|
self,
|
|
stream::{ReadyExt, TryIgnore},
|
|
};
|
|
use database::{Database, Deserialized, Json, Map};
|
|
use futures::Stream;
|
|
use ruma::OwnedUserId;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
pub(super) struct Data {
|
|
registrationtoken_info: Arc<Map>,
|
|
}
|
|
|
|
/// Metadata of a registration token.
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct DatabaseTokenInfo {
|
|
/// The admin user who created this token.
|
|
pub creator: OwnedUserId,
|
|
/// The number of times this token has been used to create an account.
|
|
pub uses: u64,
|
|
/// When this token will expire, if it expires.
|
|
pub expires: Option<TokenExpires>,
|
|
}
|
|
|
|
impl DatabaseTokenInfo {
|
|
pub(super) fn new(creator: OwnedUserId, expires: Option<TokenExpires>) -> Self {
|
|
Self { creator, uses: 0, expires }
|
|
}
|
|
|
|
/// Determine whether this token info represents a valid token, i.e. one
|
|
/// that has not expired according to its [`Self::expires`] property. If
|
|
/// [`Self::expires`] is [`None`], this function will always return `true`.
|
|
#[must_use]
|
|
pub fn is_valid(&self) -> bool {
|
|
match self.expires {
|
|
| Some(TokenExpires::AfterUses(max_uses)) => self.uses < max_uses,
|
|
| Some(TokenExpires::AfterTime(expiry_time)) => {
|
|
let now = SystemTime::now();
|
|
|
|
expiry_time >= now
|
|
},
|
|
| None => true,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Display for DatabaseTokenInfo {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "Token created by {} and used {} times. ", &self.creator, self.uses)?;
|
|
if let Some(expires) = &self.expires {
|
|
write!(f, "{expires}.")?;
|
|
} else {
|
|
write!(f, "Never expires.")?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub enum TokenExpires {
|
|
AfterUses(u64),
|
|
AfterTime(SystemTime),
|
|
}
|
|
|
|
impl std::fmt::Display for TokenExpires {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
| Self::AfterUses(max_uses) => write!(f, "Expires after {max_uses} uses"),
|
|
| Self::AfterTime(max_age) => {
|
|
let now = SystemTime::now();
|
|
let formatted_expiry = utils::time::format(*max_age, "%+");
|
|
|
|
match max_age.duration_since(now) {
|
|
| Ok(duration) => write!(
|
|
f,
|
|
"Expires in {} ({formatted_expiry})",
|
|
utils::time::pretty(duration)
|
|
),
|
|
| Err(_) => write!(f, "Expired at {formatted_expiry}"),
|
|
}
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Data {
|
|
pub(super) fn new(db: &Arc<Database>) -> Self {
|
|
Self {
|
|
registrationtoken_info: db["registrationtoken_info"].clone(),
|
|
}
|
|
}
|
|
|
|
/// Associate a registration token with its metadata in the database.
|
|
pub(super) fn save_token(&self, token: &str, info: &DatabaseTokenInfo) {
|
|
self.registrationtoken_info.raw_put(token, Json(info));
|
|
}
|
|
|
|
/// Delete a registration token.
|
|
pub(super) fn revoke_token(&self, token: &str) { self.registrationtoken_info.remove(token); }
|
|
|
|
/// Look up a registration token's metadata.
|
|
pub(super) async fn lookup_token_info(&self, token: &str) -> Option<DatabaseTokenInfo> {
|
|
self.registrationtoken_info
|
|
.get(token)
|
|
.await
|
|
.deserialized()
|
|
.ok()
|
|
}
|
|
|
|
/// Iterate over all valid tokens and delete expired ones.
|
|
pub(super) fn iterate_and_clean_tokens(
|
|
&self,
|
|
) -> impl Stream<Item = (&str, DatabaseTokenInfo)> + Send + '_ {
|
|
self.registrationtoken_info
|
|
.stream()
|
|
.ignore_err()
|
|
.ready_filter_map(|item: (&str, DatabaseTokenInfo)| {
|
|
if item.1.is_valid() {
|
|
Some(item)
|
|
} else {
|
|
self.registrationtoken_info.remove(item.0);
|
|
None
|
|
}
|
|
})
|
|
}
|
|
}
|