feat(token): add refresh token verification to TokenService
feat(bootstrap): create temporary settings and config files to prevent data loss refactor(edit_log): switch from Vec to VecDeque for efficient memory management fix(gateway): ensure store directories are created before starting the API server refactor(bgbash): implement a global singleton for BashControl feat(auth): enhance session authentication middleware to use SessionRepository fix(edit_log_repo): update to use VecDeque for in-memory edit log storage fix(memory_repo): add newline escaping for frontmatter fields fix(session_lock_repo): improve error handling for lock file operations fix(bash_tools): prevent path traversal in job_id argument refactor(delete): enforce empty directory deletion in file system tools fix(edit): optimize string replacement to only replace the first occurrence fix(git_cred): improve credential management with piped input to git commands feat(git_operator): add safety filter to block destructive git operations fix(shell): register background jobs in Bash control feat(spawn): add access tier specification for pipeline stages refactor(hive_mind): run directives concurrently for improved performance fix(auth): update refresh token verification in the refresh handler fix(chat): optimize LLM client usage based on model matching fix(conversations): enhance message deletion to target specific indices feat(api): add JWT authentication middleware for all API routes fix(state): implement refresh token verification in JwtTokenService fix(daemon): improve usage tracking with saturating addition fix(tui): handle compacted messages in the TUI state management
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
//! Authentication middleware — session-lock based auth for Axum.
|
||||
//!
|
||||
//! Validates `X-Session-Id` header against the `SessionRepository` before
|
||||
//! forwarding the request to the inner service.
|
||||
|
||||
use std::future::Future;
|
||||
use std::path::PathBuf;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use axum::body::Body;
|
||||
@@ -9,6 +14,7 @@ use axum::http::{Request, Response, StatusCode};
|
||||
use axum::response::IntoResponse;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tower::{Layer, Service};
|
||||
use zesdex_domain::auth::{SessionId, SessionRepository};
|
||||
|
||||
/// Identity extracted from a validated session.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -30,40 +36,50 @@ impl SessionIdentity {
|
||||
}
|
||||
|
||||
/// Tower Layer that produces SessionAuthMiddleware services.
|
||||
///
|
||||
/// Holds a reference to the `SessionRepository` and the base directory
|
||||
/// needed to validate session IDs.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SessionAuthLayer;
|
||||
pub struct SessionAuthLayer<R: SessionRepository + Send + Sync + 'static> {
|
||||
base_dir: PathBuf,
|
||||
repo: Arc<R>,
|
||||
}
|
||||
|
||||
impl SessionAuthLayer {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
impl<R: SessionRepository + Send + Sync + 'static> SessionAuthLayer<R> {
|
||||
pub fn new(base_dir: PathBuf, repo: Arc<R>) -> Self {
|
||||
Self { base_dir, repo }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SessionAuthLayer {
|
||||
fn default() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Layer<S> for SessionAuthLayer {
|
||||
type Service = SessionAuthMiddleware<S>;
|
||||
impl<S, R> Layer<S> for SessionAuthLayer<R>
|
||||
where
|
||||
R: SessionRepository + Send + Sync + 'static,
|
||||
{
|
||||
type Service = SessionAuthMiddleware<S, R>;
|
||||
|
||||
fn layer(&self, inner: S) -> Self::Service {
|
||||
SessionAuthMiddleware { inner }
|
||||
SessionAuthMiddleware {
|
||||
inner,
|
||||
base_dir: self.base_dir.clone(),
|
||||
repo: self.repo.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Tower Service that validates X-Session-Id before forwarding.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SessionAuthMiddleware<S> {
|
||||
pub struct SessionAuthMiddleware<S, R: SessionRepository + Send + Sync + 'static> {
|
||||
inner: S,
|
||||
base_dir: PathBuf,
|
||||
repo: Arc<R>,
|
||||
}
|
||||
|
||||
impl<S, ReqBody> Service<Request<ReqBody>> for SessionAuthMiddleware<S>
|
||||
impl<S, ReqBody, R> Service<Request<ReqBody>> for SessionAuthMiddleware<S, R>
|
||||
where
|
||||
S: Service<Request<ReqBody>, Response = Response<Body>> + Send + 'static,
|
||||
S::Future: Send + 'static,
|
||||
ReqBody: Send + 'static,
|
||||
R: SessionRepository + Send + Sync + 'static,
|
||||
{
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
@@ -81,18 +97,24 @@ where
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.map(|s| s.to_string());
|
||||
|
||||
if session_id.as_deref() != Some("valid-session") {
|
||||
// In production, this validates against the store
|
||||
return Box::pin(async move {
|
||||
Ok((
|
||||
StatusCode::UNAUTHORIZED,
|
||||
"missing or invalid X-Session-Id header",
|
||||
)
|
||||
.into_response())
|
||||
});
|
||||
// Validate the session against the repository.
|
||||
match session_id {
|
||||
Some(sid) => match SessionId::new(&sid) {
|
||||
Ok(id) => match self.repo.load_session(&self.base_dir, &id) {
|
||||
Ok(_session) => {
|
||||
// Session is valid — forward the request.
|
||||
let fut = self.inner.call(req);
|
||||
return Box::pin(fut);
|
||||
}
|
||||
Err(_) => { /* fall through to 401 */ }
|
||||
},
|
||||
Err(_) => { /* fall through to 401 */ }
|
||||
},
|
||||
None => { /* fall through to 401 */ }
|
||||
}
|
||||
|
||||
let fut = self.inner.call(req);
|
||||
Box::pin(fut)
|
||||
Box::pin(async move {
|
||||
Ok((StatusCode::UNAUTHORIZED, "missing or invalid X-Session-Id header").into_response())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user