From 188eb4872c8f8d2f3f4ee831f823be48800dfb08 Mon Sep 17 00:00:00 2001 From: Min Shao Date: Mon, 12 Feb 2024 12:56:22 -0800 Subject: [PATCH] Update `access_token` interface --- Cargo.toml | 2 +- src/auth/store.rs | 51 +++++++------------------------------- src/graphql/account.rs | 56 ++++++++++++++++++++++-------------------- 3 files changed, 40 insertions(+), 69 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b11dd38a..e38d8494 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ oinq = { git = "https://github.com/petabi/oinq.git", tag = "0.9.1" } reqwest = { version = "0.11", default-features = false, features = [ "rustls-tls-native-roots", ] } -review-database = { git = "https://github.com/petabi/review-database.git", rev = "cf694f70" } +review-database = { git = "https://github.com/petabi/review-database.git", rev = "5374afa9" } roxy = { git = "https://github.com/aicers/roxy.git", tag = "0.2.1" } rustls = "0.21" rustls-native-certs = "0.6" diff --git a/src/auth/store.rs b/src/auth/store.rs index 038ba074..a53e41f0 100644 --- a/src/auth/store.rs +++ b/src/auth/store.rs @@ -1,8 +1,6 @@ use super::AuthError; use crate::Store; -use anyhow::{anyhow, bail, Result}; -use bincode::Options; -use std::collections::HashSet; +use anyhow::{anyhow, Result}; /// Inserts a token into the store. /// @@ -11,20 +9,7 @@ use std::collections::HashSet; /// Returns an error if the tokens in the store are invalid, if the token cannot be serialized, or /// if the store cannot be accessed. pub fn insert_token(store: &Store, token: &str, username: &str) -> Result<()> { - let map = store.access_token_map(); - let tokens = map.get(username.as_bytes())?; - let value = if let Some(tokens) = tokens { - let mut tokens = - bincode::DefaultOptions::new().deserialize::>(tokens.as_ref())?; - tokens.insert(token.to_string()); - tokens - } else { - HashSet::from([token.to_string()]) - }; - let value = bincode::DefaultOptions::new().serialize(&value)?; - map.put(username.as_bytes(), &value)?; - - Ok(()) + store.access_token_map().insert(username, token) } /// Revokes a token from the store. @@ -36,20 +21,10 @@ pub fn insert_token(store: &Store, token: &str, username: &str) -> Result<()> { pub fn revoke_token(store: &Store, token: &str) -> Result<()> { let decoded_token = super::decode_token(token)?; let username = decoded_token.sub; - let map = store.access_token_map(); - let value = map - .get(username.as_bytes())? - .ok_or_else(|| anyhow!("The given token does not exist"))?; - let mut tokens = - bincode::DefaultOptions::new().deserialize::>(value.as_ref())?; - if tokens.contains(token) { - tokens.remove(token); - let value = bincode::DefaultOptions::new().serialize(&tokens)?; - map.put(username.as_bytes(), &value)?; - Ok(()) - } else { - bail!("The given token does not exist"); - } + store + .access_token_map() + .revoke(&username, token) + .map_err(|_| anyhow!("The given token does not exist")) } pub(super) fn token_exists_in_store( @@ -57,16 +32,8 @@ pub(super) fn token_exists_in_store( token: &str, username: &str, ) -> Result { - let value = store + store .access_token_map() - .get(username.as_bytes()) - .map_err(|_| AuthError::InvalidToken("Token not found in the database".into()))?; - if let Some(value) = value { - let tokens = bincode::DefaultOptions::new() - .deserialize::>(value.as_ref()) - .map_err(|e| AuthError::Other(format!("An unexpected value in the database: {e}")))?; - Ok(tokens.contains(token)) - } else { - Ok(false) - } + .contains(username, token) + .map_err(|_| AuthError::InvalidToken("Token not found in the database".into())) } diff --git a/src/graphql/account.rs b/src/graphql/account.rs index 5240244a..b659f9e9 100644 --- a/src/graphql/account.rs +++ b/src/graphql/account.rs @@ -9,13 +9,10 @@ use chrono::{DateTime, NaiveDateTime, TimeZone, Utc}; use review_database::{ self as database, types::{self}, - Direction, IterableMap, Store, + Direction, Store, }; use serde::{Deserialize, Serialize}; -use std::{ - collections::HashSet, - net::{AddrParseError, IpAddr}, -}; +use std::net::{AddrParseError, IpAddr}; use tracing::info; #[allow(clippy::module_name_repetitions)] @@ -76,33 +73,40 @@ impl AccountQuery { #[graphql(guard = "RoleGuard::new(super::Role::SystemAdministrator) .or(RoleGuard::new(super::Role::SecurityAdministrator))")] async fn signed_in_account_list(&self, ctx: &Context<'_>) -> Result> { + use review_database::KeyValueIterable; + use std::collections::HashMap; + let store = crate::graphql::get_store(ctx).await?; let map = store.access_token_map(); let signed = map - .iter_forward()? - .filter_map(|(key, value)| { - bincode::DefaultOptions::new() - .deserialize::>(&value) + .iter(Direction::Forward, None) + .filter_map(|e| { + let e = e.ok()?; + let username = e.username; + let exp_time = decode_token(&e.token) .ok() - .map(|tokens| SignedInAccount { - username: String::from_utf8_lossy(&key).into_owned(), - expire_times: tokens - .iter() - .filter_map(|token| { - decode_token(token).ok().and_then(|decoded| { - let time = Utc.timestamp_nanos(decoded.exp * 1_000_000_000); - if Utc::now() < time { - Some(time) - } else { - None - } - }) - }) - .collect::>>(), - }) + .map(|t| Utc.timestamp_nanos(t.exp * 1_000_000_000))?; + if Utc::now() < exp_time { + Some((username, exp_time)) + } else { + None + } + }) + .fold( + HashMap::new(), + |mut res: HashMap<_, Vec<_>>, (username, time)| { + let e = res.entry(username).or_default(); + e.push(time); + res + }, + ) + .into_iter() + .map(|(username, expire_times)| SignedInAccount { + username, + expire_times, }) - .collect::>(); + .collect::>(); Ok(signed) }