diff --git a/core/startos/src/auth.rs b/core/startos/src/auth.rs index 03005998f..68e16c244 100644 --- a/core/startos/src/auth.rs +++ b/core/startos/src/auth.rs @@ -185,6 +185,8 @@ pub struct LoginParams { #[serde(rename = "__auth_userAgent")] // from Auth middleware user_agent: Option, #[serde(default)] + ephemeral: bool, + #[serde(default)] #[ts(type = "any")] metadata: Value, } @@ -195,28 +197,46 @@ pub async fn login_impl( LoginParams { password, user_agent, + ephemeral, metadata, }: LoginParams, ) -> Result { let password = password.unwrap_or_default().decrypt(&ctx)?; - ctx.db - .mutate(|db| { - check_password_against_db(db, &password)?; - let hash_token = HashSessionToken::new(); - db.as_private_mut().as_sessions_mut().insert( - hash_token.hashed(), - &Session { + if ephemeral { + check_password_against_db(&ctx.db.peek().await, &password)?; + let hash_token = HashSessionToken::new(); + ctx.ephemeral_sessions.mutate(|s| { + s.0.insert( + hash_token.hashed().clone(), + Session { logged_in: Utc::now(), last_active: Utc::now(), user_agent, metadata, }, - )?; + ) + }); + Ok(hash_token.to_login_res()) + } else { + ctx.db + .mutate(|db| { + check_password_against_db(db, &password)?; + let hash_token = HashSessionToken::new(); + db.as_private_mut().as_sessions_mut().insert( + hash_token.hashed(), + &Session { + logged_in: Utc::now(), + last_active: Utc::now(), + user_agent, + metadata, + }, + )?; - Ok(hash_token.to_login_res()) - }) - .await + Ok(hash_token.to_login_res()) + }) + .await + } } #[derive(Deserialize, Serialize, Parser, TS)] @@ -329,9 +349,15 @@ pub async fn list( ctx: RpcContext, ListParams { session, .. }: ListParams, ) -> Result { + let mut sessions = ctx.db.peek().await.into_private().into_sessions().de()?; + ctx.ephemeral_sessions.mutate(|s| { + sessions + .0 + .extend(s.0.iter().map(|(k, v)| (k.clone(), v.clone()))) + }); Ok(SessionList { current: HashSessionToken::from_token(session).hashed().clone(), - sessions: ctx.db.peek().await.into_private().into_sessions().de()?, + sessions, }) } diff --git a/core/startos/src/context/rpc.rs b/core/startos/src/context/rpc.rs index cea2059d3..0d00abb3a 100644 --- a/core/startos/src/context/rpc.rs +++ b/core/startos/src/context/rpc.rs @@ -17,6 +17,7 @@ use tracing::instrument; use super::setup::CURRENT_SECRET; use crate::account::AccountInfo; +use crate::auth::Sessions; use crate::context::config::ServerConfig; use crate::db::model::Database; use crate::dependencies::compute_dependency_config_errs; @@ -34,6 +35,7 @@ use crate::service::ServiceMap; use crate::shutdown::Shutdown; use crate::system::get_mem_info; use crate::util::lshw::{lshw, LshwDevice}; +use crate::util::sync::SyncMutex; pub struct RpcContextSeed { is_closed: AtomicBool, @@ -42,6 +44,7 @@ pub struct RpcContextSeed { pub ethernet_interface: String, pub datadir: PathBuf, pub disk_guid: Arc, + pub ephemeral_sessions: SyncMutex, pub db: TypedPatchDb, pub sync_db: watch::Sender, pub account: RwLock, @@ -213,6 +216,7 @@ impl RpcContext { find_eth_iface().await? }, disk_guid, + ephemeral_sessions: SyncMutex::new(Sessions::new()), sync_db: watch::Sender::new(db.sequence().await), db, account: RwLock::new(account), diff --git a/core/startos/src/middleware/auth.rs b/core/startos/src/middleware/auth.rs index fd60894db..9b04afb38 100644 --- a/core/startos/src/middleware/auth.rs +++ b/core/startos/src/middleware/auth.rs @@ -51,6 +51,11 @@ impl HasLoggedOutSessions { for sid in &to_log_out { ctx.open_authed_continuations.kill(sid) } + ctx.ephemeral_sessions.mutate(|s| { + for sid in &to_log_out { + s.0.remove(sid); + } + }); ctx.db .mutate(|db| { let sessions = db.as_private_mut().as_sessions_mut(); @@ -110,20 +115,29 @@ impl HasValidSession { ctx: &RpcContext, ) -> Result { let session_hash = session_token.hashed(); - ctx.db - .mutate(|db| { - db.as_private_mut() - .as_sessions_mut() - .as_idx_mut(session_hash) - .ok_or_else(|| { - Error::new(eyre!("UNAUTHORIZED"), crate::ErrorKind::Authorization) - })? - .mutate(|s| { - s.last_active = Utc::now(); - Ok(()) - }) - }) - .await?; + if !ctx.ephemeral_sessions.mutate(|s| { + if let Some(session) = s.0.get_mut(session_hash) { + session.last_active = Utc::now(); + true + } else { + false + } + }) { + ctx.db + .mutate(|db| { + db.as_private_mut() + .as_sessions_mut() + .as_idx_mut(session_hash) + .ok_or_else(|| { + Error::new(eyre!("UNAUTHORIZED"), crate::ErrorKind::Authorization) + })? + .mutate(|s| { + s.last_active = Utc::now(); + Ok(()) + }) + }) + .await?; + } Ok(Self(SessionType::Session(session_token))) } diff --git a/core/startos/src/util/mod.rs b/core/startos/src/util/mod.rs index 1641e4496..fab0b127d 100644 --- a/core/startos/src/util/mod.rs +++ b/core/startos/src/util/mod.rs @@ -49,6 +49,7 @@ pub mod net; pub mod rpc; pub mod rpc_client; pub mod serde; +pub mod sync; #[derive(Clone, Copy, Debug, ::serde::Deserialize, ::serde::Serialize)] pub enum Never {} diff --git a/core/startos/src/util/sync.rs b/core/startos/src/util/sync.rs new file mode 100644 index 000000000..f8ec425e0 --- /dev/null +++ b/core/startos/src/util/sync.rs @@ -0,0 +1,9 @@ +pub struct SyncMutex(std::sync::Mutex); +impl SyncMutex { + pub fn new(t: T) -> Self { + Self(std::sync::Mutex::new(t)) + } + pub fn mutate U, U>(&self, f: F) -> U { + f(&mut *self.0.lock().unwrap()) + } +} diff --git a/sdk/lib/osBindings/LoginParams.ts b/sdk/lib/osBindings/LoginParams.ts index 272f7ed07..acaf5b8a1 100644 --- a/sdk/lib/osBindings/LoginParams.ts +++ b/sdk/lib/osBindings/LoginParams.ts @@ -1,4 +1,8 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { PasswordType } from "./PasswordType" -export type LoginParams = { password: PasswordType | null; metadata: any } +export type LoginParams = { + password: PasswordType | null + ephemeral: boolean + metadata: any +} diff --git a/web/projects/ui/src/app/pages/login/login.page.ts b/web/projects/ui/src/app/pages/login/login.page.ts index 065a4cc7f..29d4321f8 100644 --- a/web/projects/ui/src/app/pages/login/login.page.ts +++ b/web/projects/ui/src/app/pages/login/login.page.ts @@ -40,6 +40,7 @@ export class LoginPage { await this.api.login({ password: this.password, metadata: { platforms: getPlatforms() }, + ephemeral: window.location.host === 'localhost', }) this.password = '' diff --git a/web/projects/ui/src/app/services/api/api.types.ts b/web/projects/ui/src/app/services/api/api.types.ts index 770792328..eac7cd7f4 100644 --- a/web/projects/ui/src/app/services/api/api.types.ts +++ b/web/projects/ui/src/app/services/api/api.types.ts @@ -33,6 +33,7 @@ export module RR { export type LoginReq = { password: string metadata: SessionMetadata + ephemeral?: boolean } // auth.login - unauthed export type loginRes = null diff --git a/web/projects/ui/src/app/services/config.service.ts b/web/projects/ui/src/app/services/config.service.ts index a148112f0..69fafe815 100644 --- a/web/projects/ui/src/app/services/config.service.ts +++ b/web/projects/ui/src/app/services/config.service.ts @@ -73,8 +73,6 @@ export class ConfigService { const hostnameInfo = hosts[ui.addressInfo.hostId]?.hostnameInfo[ui.addressInfo.internalPort] - console.debug(hostnameInfo) - if (!hostnameInfo) return '' const addressInfo = ui.addressInfo