use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::SystemTime;
use crate::error::{
ConstraintViolationError, ConstraintViolationType, InternalError, InvalidArgumentError,
InvalidStateError,
};
use super::{
InsertableOAuthUserSession, OAuthUser, OAuthUserIter, OAuthUserSession, OAuthUserSessionStore,
OAuthUserSessionStoreError,
};
#[derive(Default, Clone)]
pub struct MemoryOAuthUserSessionStore {
internal: Arc<Mutex<Internal>>,
}
impl MemoryOAuthUserSessionStore {
pub fn new() -> Self {
Self::default()
}
}
impl OAuthUserSessionStore for MemoryOAuthUserSessionStore {
fn add_session(
&self,
session: InsertableOAuthUserSession,
) -> Result<(), OAuthUserSessionStoreError> {
let mut internal = self.internal.lock().map_err(|_| {
OAuthUserSessionStoreError::Internal(InternalError::with_message(
"Cannot access OAuth user session store: mutex lock poisoned".to_string(),
))
})?;
if internal
.sessions
.contains_key(session.splinter_access_token())
{
return Err(OAuthUserSessionStoreError::ConstraintViolation(
ConstraintViolationError::with_violation_type(ConstraintViolationType::Unique),
));
}
if !internal.users.contains_key(session.subject()) {
internal.users.insert(
session.subject().to_string(),
OAuthUser::new(session.subject().to_string()),
);
}
internal
.sessions
.insert(session.splinter_access_token().into(), session.into());
Ok(())
}
fn update_session(
&self,
session: InsertableOAuthUserSession,
) -> Result<(), OAuthUserSessionStoreError> {
let mut internal = self.internal.lock().map_err(|_| {
OAuthUserSessionStoreError::Internal(InternalError::with_message(
"Cannot access OAuth user session store: mutex lock poisoned".to_string(),
))
})?;
match internal.sessions.get(session.splinter_access_token()) {
Some(existing_session) => {
if session.subject() != existing_session.subject {
Err(OAuthUserSessionStoreError::InvalidArgument(
InvalidArgumentError::new(
"session".to_string(),
"Cannot update the 'subject' field for an OAuth user session".into(),
),
))
} else {
internal
.sessions
.insert(session.splinter_access_token().into(), session.into());
Ok(())
}
}
None => Err(OAuthUserSessionStoreError::InvalidState(
InvalidStateError::with_message(
"An OAuth user session for the given Splinter access token does not exist"
.to_string(),
),
)),
}
}
fn remove_session(
&self,
splinter_access_token: &str,
) -> Result<(), OAuthUserSessionStoreError> {
self.internal
.lock()
.map_err(|_| {
OAuthUserSessionStoreError::Internal(InternalError::with_message(
"Cannot access OAuth user session store: mutex lock poisoned".to_string(),
))
})?
.sessions
.remove(splinter_access_token)
.map(|_| ())
.ok_or_else(|| {
OAuthUserSessionStoreError::InvalidState(InvalidStateError::with_message(
"An OAuth user session for the given Splinter access token does not exist"
.to_string(),
))
})
}
fn get_session(
&self,
splinter_access_token: &str,
) -> Result<Option<OAuthUserSession>, OAuthUserSessionStoreError> {
let internal = self.internal.lock().map_err(|_| {
OAuthUserSessionStoreError::Internal(InternalError::with_message(
"Cannot access OAuth user session store: mutex lock poisoned".to_string(),
))
})?;
internal
.sessions
.get(splinter_access_token)
.cloned()
.map(|session| {
let InternalOAuthUserSession {
splinter_access_token,
subject,
oauth_access_token,
oauth_refresh_token,
last_authenticated,
} = session;
let user = internal.users.get(&subject).cloned().ok_or_else(|| {
OAuthUserSessionStoreError::Internal(InternalError::with_message(
"Unknown session subject".to_string(),
))
})?;
Ok(OAuthUserSession {
splinter_access_token,
user,
oauth_access_token,
oauth_refresh_token,
last_authenticated,
})
})
.transpose()
}
fn get_user(&self, subject: &str) -> Result<Option<OAuthUser>, OAuthUserSessionStoreError> {
Ok(self
.internal
.lock()
.map_err(|_| {
OAuthUserSessionStoreError::Internal(InternalError::with_message(
"Cannot access OAuth user session store: mutex lock poisoned".to_string(),
))
})?
.users
.get(subject)
.cloned())
}
fn list_users(&self) -> Result<OAuthUserIter, OAuthUserSessionStoreError> {
let internal = self.internal.lock().map_err(|_| {
OAuthUserSessionStoreError::Internal(InternalError::with_message(
"Cannot access OAuth user session store: mutex lock poisoned".to_string(),
))
})?;
let users = internal.users.values().cloned().collect::<Vec<OAuthUser>>();
Ok(OAuthUserIter::new(users))
}
fn clone_box(&self) -> Box<dyn OAuthUserSessionStore> {
Box::new(self.clone())
}
}
#[derive(Default)]
struct Internal {
pub users: HashMap<String, OAuthUser>,
pub sessions: HashMap<String, InternalOAuthUserSession>,
}
#[derive(Clone)]
struct InternalOAuthUserSession {
pub splinter_access_token: String,
pub subject: String,
pub oauth_access_token: String,
pub oauth_refresh_token: Option<String>,
pub last_authenticated: SystemTime,
}
impl From<InsertableOAuthUserSession> for InternalOAuthUserSession {
fn from(session: InsertableOAuthUserSession) -> Self {
let InsertableOAuthUserSession {
splinter_access_token,
subject,
oauth_access_token,
oauth_refresh_token,
} = session;
Self {
splinter_access_token,
subject,
oauth_access_token,
oauth_refresh_token,
last_authenticated: SystemTime::now(),
}
}
}