use std::collections::HashMap;
use std::fmt;
use std::future::Future;
use std::sync::{Arc, Mutex};
use crate::authorization::AuthorizationCodeRecord;
use crate::client::{Client, ClientId};
use crate::device::{DeviceGrant, DeviceGrantState};
use crate::token::{IssuedToken, RefreshTokenRecord};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StorageError(pub String);
impl StorageError {
pub fn new(msg: impl Into<String>) -> Self {
StorageError(msg.into())
}
}
impl fmt::Display for StorageError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "storage error: {}", self.0)
}
}
impl std::error::Error for StorageError {}
pub trait Storage: Send + Sync {
fn get_client(
&self,
client_id: &ClientId,
) -> impl Future<Output = Result<Option<Arc<Client>>, StorageError>> + Send;
fn put_client(&self, client: Client) -> impl Future<Output = Result<(), StorageError>> + Send;
fn delete_client(
&self,
client_id: &ClientId,
) -> impl Future<Output = Result<bool, StorageError>> + Send;
fn put_device_grant(
&self,
grant: DeviceGrant,
) -> impl Future<Output = Result<(), StorageError>> + Send;
fn get_device_grant(
&self,
device_code: &str,
) -> impl Future<Output = Result<Option<DeviceGrant>, StorageError>> + Send;
fn find_device_grant_by_user_code(
&self,
normalized_user_code: &str,
) -> impl Future<Output = Result<Option<DeviceGrant>, StorageError>> + Send;
fn take_device_grant(
&self,
device_code: &str,
) -> impl Future<Output = Result<Option<DeviceGrant>, StorageError>> + Send;
fn compare_and_swap_device_grant(
&self,
expected: &DeviceGrantState,
updated: DeviceGrant,
) -> impl Future<Output = Result<bool, StorageError>> + Send;
fn put_authorization_code(
&self,
record: AuthorizationCodeRecord,
) -> impl Future<Output = Result<(), StorageError>> + Send;
fn take_authorization_code(
&self,
code: &str,
) -> impl Future<Output = Result<Option<AuthorizationCodeRecord>, StorageError>> + Send;
#[cfg(feature = "par")]
fn put_pushed_authorization_request(
&self,
record: crate::par::PushedAuthorizationRequest,
) -> impl Future<Output = Result<(), StorageError>> + Send;
#[cfg(feature = "par")]
fn take_pushed_authorization_request(
&self,
request_uri: &str,
) -> impl Future<Output = Result<Option<crate::par::PushedAuthorizationRequest>, StorageError>> + Send;
fn put_token(
&self,
token: IssuedToken,
) -> impl Future<Output = Result<(), StorageError>> + Send;
fn get_token(
&self,
access_token: &str,
) -> impl Future<Output = Result<Option<Arc<IssuedToken>>, StorageError>> + Send;
fn delete_token(
&self,
access_token: &str,
) -> impl Future<Output = Result<(), StorageError>> + Send;
fn put_refresh_token(
&self,
record: RefreshTokenRecord,
) -> impl Future<Output = Result<(), StorageError>> + Send;
fn get_refresh_token(
&self,
refresh_token: &str,
) -> impl Future<Output = Result<Option<Arc<RefreshTokenRecord>>, StorageError>> + Send;
fn take_refresh_token(
&self,
refresh_token: &str,
) -> impl Future<Output = Result<Option<RefreshTokenRecord>, StorageError>> + Send;
fn revoke_token_family(
&self,
family_id: &str,
) -> impl Future<Output = Result<u64, StorageError>> + Send;
#[cfg(feature = "consent")]
fn put_consent(
&self,
record: crate::consent::ConsentRecord,
) -> impl Future<Output = Result<(), StorageError>> + Send;
#[cfg(feature = "consent")]
fn get_consent(
&self,
consent_id: &str,
) -> impl Future<Output = Result<Option<Arc<crate::consent::ConsentRecord>>, StorageError>> + Send;
#[cfg(feature = "consent")]
fn find_consent(
&self,
client_id: &ClientId,
subject: &str,
) -> impl Future<Output = Result<Option<Arc<crate::consent::ConsentRecord>>, StorageError>> + Send;
#[cfg(feature = "consent")]
fn consents_for_subject(
&self,
subject: &str,
) -> impl Future<Output = Result<Vec<Arc<crate::consent::ConsentRecord>>, StorageError>> + Send;
#[cfg(feature = "consent")]
fn revoke_consent(
&self,
consent_id: &str,
) -> impl Future<Output = Result<u64, StorageError>> + Send;
#[cfg(any(feature = "client_assertion", feature = "dpop"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "client_assertion", feature = "dpop"))))]
fn claim_replay_id(
&self,
id: &str,
expires_at: std::time::SystemTime,
) -> impl Future<Output = Result<bool, StorageError>> + Send;
fn sweep_expired(
&self,
now: std::time::SystemTime,
) -> impl Future<Output = Result<u64, StorageError>> + Send;
}
#[derive(Default)]
struct MemoryInner {
clients: HashMap<String, Arc<Client>>,
device_by_code: HashMap<String, DeviceGrant>,
user_code_index: HashMap<String, String>,
codes: HashMap<String, AuthorizationCodeRecord>,
#[cfg(feature = "par")]
pushed: HashMap<String, crate::par::PushedAuthorizationRequest>,
tokens: HashMap<String, Arc<IssuedToken>>,
refresh: HashMap<String, Arc<RefreshTokenRecord>>,
#[cfg(feature = "consent")]
consents: HashMap<String, Arc<crate::consent::ConsentRecord>>,
#[cfg(any(feature = "client_assertion", feature = "dpop"))]
replay_ids: HashMap<String, std::time::SystemTime>,
}
#[derive(Default)]
pub struct MemoryStorage {
inner: Mutex<MemoryInner>,
}
impl MemoryStorage {
pub fn new() -> Self {
Self::default()
}
fn lock(&self) -> std::sync::MutexGuard<'_, MemoryInner> {
self.inner.lock().unwrap_or_else(|e| e.into_inner())
}
}
impl Storage for MemoryStorage {
async fn get_client(&self, client_id: &ClientId) -> Result<Option<Arc<Client>>, StorageError> {
Ok(self.lock().clients.get(client_id.as_str()).cloned())
}
async fn put_client(&self, client: Client) -> Result<(), StorageError> {
self.lock()
.clients
.insert(client.client_id.as_str().to_string(), Arc::new(client));
Ok(())
}
async fn delete_client(&self, client_id: &ClientId) -> Result<bool, StorageError> {
let mut g = self.lock();
let existed = g.clients.remove(client_id.as_str()).is_some();
g.tokens.retain(|_, t| &t.client_id != client_id);
g.refresh.retain(|_, r| &r.client_id != client_id);
g.codes.retain(|_, c| &c.client_id != client_id);
#[cfg(feature = "par")]
g.pushed.retain(|_, p| &p.client_id != client_id);
g.device_by_code.retain(|_, d| &d.client_id != client_id);
#[cfg(feature = "consent")]
g.consents.retain(|_, c| &c.client_id != client_id);
let live = &g.device_by_code;
let stale: Vec<String> = g
.user_code_index
.iter()
.filter(|(_, dc)| !live.contains_key(*dc))
.map(|(uc, _)| uc.clone())
.collect();
for uc in stale {
g.user_code_index.remove(&uc);
}
Ok(existed)
}
async fn put_device_grant(&self, grant: DeviceGrant) -> Result<(), StorageError> {
let mut g = self.lock();
let normalized = crate::device::normalize_user_code(&grant.user_code);
if let Some(owner) = g.user_code_index.get(&normalized) {
if owner != &grant.device_code {
return Err(StorageError::new(
"user code is already indexed for a different device_code",
));
}
}
if let Some(previous) = g.device_by_code.get(&grant.device_code) {
let previous_normalized = crate::device::normalize_user_code(&previous.user_code);
if previous_normalized != normalized {
g.user_code_index.remove(&previous_normalized);
}
}
g.user_code_index
.insert(normalized, grant.device_code.clone());
g.device_by_code.insert(grant.device_code.clone(), grant);
Ok(())
}
async fn get_device_grant(
&self,
device_code: &str,
) -> Result<Option<DeviceGrant>, StorageError> {
Ok(self.lock().device_by_code.get(device_code).cloned())
}
async fn find_device_grant_by_user_code(
&self,
normalized_user_code: &str,
) -> Result<Option<DeviceGrant>, StorageError> {
let g = self.lock();
Ok(g.user_code_index
.get(normalized_user_code)
.and_then(|dc| g.device_by_code.get(dc))
.cloned())
}
async fn take_device_grant(
&self,
device_code: &str,
) -> Result<Option<DeviceGrant>, StorageError> {
let mut g = self.lock();
let grant = g.device_by_code.remove(device_code);
if let Some(grant) = &grant {
let normalized = crate::device::normalize_user_code(&grant.user_code);
g.user_code_index.remove(&normalized);
}
Ok(grant)
}
async fn compare_and_swap_device_grant(
&self,
expected: &DeviceGrantState,
updated: DeviceGrant,
) -> Result<bool, StorageError> {
let mut g = self.lock();
match g.device_by_code.get(&updated.device_code) {
Some(current) if current.state == *expected => {}
_ => return Ok(false),
}
let normalized = crate::device::normalize_user_code(&updated.user_code);
if let Some(owner) = g.user_code_index.get(&normalized) {
if owner != &updated.device_code {
return Err(StorageError::new(
"user code is already indexed for a different device_code",
));
}
}
if let Some(previous) = g.device_by_code.get(&updated.device_code) {
let previous_normalized = crate::device::normalize_user_code(&previous.user_code);
if previous_normalized != normalized {
g.user_code_index.remove(&previous_normalized);
}
}
g.user_code_index
.insert(normalized, updated.device_code.clone());
g.device_by_code
.insert(updated.device_code.clone(), updated);
Ok(true)
}
async fn put_authorization_code(
&self,
record: AuthorizationCodeRecord,
) -> Result<(), StorageError> {
self.lock().codes.insert(record.code.clone(), record);
Ok(())
}
async fn take_authorization_code(
&self,
code: &str,
) -> Result<Option<AuthorizationCodeRecord>, StorageError> {
Ok(self.lock().codes.remove(code))
}
#[cfg(feature = "par")]
async fn put_pushed_authorization_request(
&self,
record: crate::par::PushedAuthorizationRequest,
) -> Result<(), StorageError> {
self.lock()
.pushed
.insert(record.request_uri.clone(), record);
Ok(())
}
#[cfg(feature = "par")]
async fn take_pushed_authorization_request(
&self,
request_uri: &str,
) -> Result<Option<crate::par::PushedAuthorizationRequest>, StorageError> {
Ok(self.lock().pushed.remove(request_uri))
}
async fn put_token(&self, token: IssuedToken) -> Result<(), StorageError> {
self.lock()
.tokens
.insert(token.access_token.clone(), Arc::new(token));
Ok(())
}
async fn get_token(
&self,
access_token: &str,
) -> Result<Option<Arc<IssuedToken>>, StorageError> {
Ok(self.lock().tokens.get(access_token).cloned())
}
async fn delete_token(&self, access_token: &str) -> Result<(), StorageError> {
self.lock().tokens.remove(access_token);
Ok(())
}
async fn put_refresh_token(&self, record: RefreshTokenRecord) -> Result<(), StorageError> {
self.lock()
.refresh
.insert(record.refresh_token.clone(), Arc::new(record));
Ok(())
}
async fn get_refresh_token(
&self,
refresh_token: &str,
) -> Result<Option<Arc<RefreshTokenRecord>>, StorageError> {
Ok(self.lock().refresh.get(refresh_token).cloned())
}
async fn take_refresh_token(
&self,
refresh_token: &str,
) -> Result<Option<RefreshTokenRecord>, StorageError> {
Ok(self
.lock()
.refresh
.remove(refresh_token)
.map(|a| Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone())))
}
async fn revoke_token_family(&self, family_id: &str) -> Result<u64, StorageError> {
let mut g = self.lock();
let before = g.tokens.len() + g.refresh.len();
g.tokens
.retain(|_, t| t.family_id.as_deref() != Some(family_id));
g.refresh.retain(|_, r| r.family_id != family_id);
Ok((before - (g.tokens.len() + g.refresh.len())) as u64)
}
#[cfg(feature = "consent")]
async fn put_consent(&self, record: crate::consent::ConsentRecord) -> Result<(), StorageError> {
self.lock()
.consents
.insert(record.consent_id.to_string(), Arc::new(record));
Ok(())
}
#[cfg(feature = "consent")]
async fn get_consent(
&self,
consent_id: &str,
) -> Result<Option<Arc<crate::consent::ConsentRecord>>, StorageError> {
Ok(self.lock().consents.get(consent_id).cloned())
}
#[cfg(feature = "consent")]
async fn find_consent(
&self,
client_id: &ClientId,
subject: &str,
) -> Result<Option<Arc<crate::consent::ConsentRecord>>, StorageError> {
Ok(self
.lock()
.consents
.values()
.find(|c| &c.client_id == client_id && c.subject.as_ref() == subject)
.cloned())
}
#[cfg(feature = "consent")]
async fn consents_for_subject(
&self,
subject: &str,
) -> Result<Vec<Arc<crate::consent::ConsentRecord>>, StorageError> {
Ok(self
.lock()
.consents
.values()
.filter(|c| c.subject.as_ref() == subject)
.cloned()
.collect())
}
#[cfg(feature = "consent")]
async fn revoke_consent(&self, consent_id: &str) -> Result<u64, StorageError> {
let mut g = self.lock();
let consent = match g.consents.remove(consent_id) {
Some(c) => c,
None => return Ok(0),
};
let client_id = &consent.client_id;
let subject: &str = consent.subject.as_ref();
let before = g.tokens.len() + g.refresh.len() + g.codes.len() + g.device_by_code.len();
g.tokens
.retain(|_, t| !(&t.client_id == client_id && t.subject.as_deref() == Some(subject)));
g.refresh
.retain(|_, r| !(&r.client_id == client_id && r.subject.as_deref() == Some(subject)));
g.codes
.retain(|_, c| !(&c.client_id == client_id && c.subject == subject));
g.device_by_code.retain(|_, d| {
!(&d.client_id == client_id
&& matches!(&d.state, DeviceGrantState::Approved { subject: s } if s == subject))
});
let live = &g.device_by_code;
let stale: Vec<String> = g
.user_code_index
.iter()
.filter(|(_, dc)| !live.contains_key(*dc))
.map(|(uc, _)| uc.clone())
.collect();
for uc in stale {
g.user_code_index.remove(&uc);
}
let after = g.tokens.len() + g.refresh.len() + g.codes.len() + g.device_by_code.len();
Ok((before - after) as u64)
}
#[cfg(any(feature = "client_assertion", feature = "dpop"))]
async fn claim_replay_id(
&self,
id: &str,
expires_at: std::time::SystemTime,
) -> Result<bool, StorageError> {
let mut g = self.lock();
if g.replay_ids.contains_key(id) {
return Ok(false);
}
g.replay_ids.insert(id.to_string(), expires_at);
Ok(true)
}
async fn sweep_expired(&self, now: std::time::SystemTime) -> Result<u64, StorageError> {
let mut g = self.lock();
let mut removed = 0u64;
let before = g.device_by_code.len();
g.device_by_code.retain(|_, grant| now < grant.expires_at);
removed += (before - g.device_by_code.len()) as u64;
let live = &g.device_by_code;
let stale: Vec<String> = g
.user_code_index
.iter()
.filter(|(_, dc)| !live.contains_key(*dc))
.map(|(uc, _)| uc.clone())
.collect();
for uc in stale {
g.user_code_index.remove(&uc);
}
let before = g.codes.len();
g.codes.retain(|_, c| now < c.expires_at);
removed += (before - g.codes.len()) as u64;
#[cfg(feature = "par")]
{
let before = g.pushed.len();
g.pushed.retain(|_, p| now < p.expires_at);
removed += (before - g.pushed.len()) as u64;
}
let before = g.tokens.len();
g.tokens.retain(|_, t| now < t.expires_at);
removed += (before - g.tokens.len()) as u64;
let before = g.refresh.len();
g.refresh.retain(|_, r| match r.expires_at {
Some(exp) => now < exp,
None => true,
});
removed += (before - g.refresh.len()) as u64;
#[cfg(any(feature = "client_assertion", feature = "dpop"))]
{
let before = g.replay_ids.len();
g.replay_ids.retain(|_, exp| now < *exp);
removed += (before - g.replay_ids.len()) as u64;
}
Ok(removed)
}
}