Files
poimen-memory/crates/mem-cli/src/auth_middleware.rs
T
rock d8c3b06cb0
CI / CI (push) Successful in 15m14s
fix: resolve 75 mem-cli compilation errors
All errors were API mismatches — handler code calling wrong method
   names, wrong argument types, or missing imports/derives. No logic
   changes. Build now passes with SQLX_OFFLINE=true.

   Key fixes:
   - embed_text -> embed_one, Vector -> Vec<f32> conversion
   - extract_token: extract auth header from HttpRequest first
   - AuthError variants aligned to actual enum definition
   - recursive async fns boxed (dfs_paths in inference + path_finder)
   - missing derives (Default, Serialize), imports (sqlx::Row, Timelike)
   - borrow-after-move: compute .len() before struct field move
   - streaming_body -> streaming with Result<Bytes> for SSE
   - CI: add SQLX_OFFLINE=true for offline builds without DB

   25 files changed, 99 insertions(+), 81 deletions(-)

Co-authored-by: rock <[email protected]>
2026-09-08 01:11:14 +00:00

122 lines
4.0 KiB
Rust

/// Auth middleware helpers for HTTP handlers
///
/// Provides utilities to:
/// 1. Validate JWT tokens from requests
/// 2. Extract claims
/// 3. Check permissions
/// 4. Return standardized auth errors
use actix_web::{HttpRequest, HttpResponse};
use serde_json::json;
use crate::auth::provider::{AuthProvider, AuthError};
use crate::auth::guard::{AuthGuard, PermissionGuard, Role};
/// Result type for auth operations
pub type AuthResult<T> = Result<T, AuthError>;
/// Extract and validate bearer token from request
pub async fn validate_request_token(
req: &HttpRequest,
auth_provider: &dyn AuthProvider,
) -> AuthResult<crate::auth::provider::Claims> {
// Extract Authorization header
let auth_header = req
.headers()
.get("Authorization")
.and_then(|h| h.to_str().ok())
.ok_or(AuthError::MissingToken)?;
// Extract token from "Bearer <token>"
let token = AuthGuard::extract_token(auth_header)?;
// Validate token with provider
auth_provider.validate_token(&token).await
}
/// Check if user has required role for resource
pub fn check_resource_role(
claims: &crate::auth::provider::Claims,
resource_type: &str,
resource_id: &str,
required_role: Role,
) -> bool {
let user_role = PermissionGuard::get_resource_role(claims, resource_type, resource_id)
.unwrap_or(Role::User);
user_role.satisfies(required_role)
}
/// Check if user belongs to required group
pub fn check_group_membership(
claims: &crate::auth::provider::Claims,
required_group: &str,
) -> bool {
PermissionGuard::check_group(claims, required_group)
}
/// Convert auth error to HTTP response
pub fn auth_error_response(error: &AuthError) -> HttpResponse {
let (status, message) = match error {
AuthError::MissingToken => ("Unauthorized", "Missing or invalid Authorization header"),
AuthError::InvalidSignature => ("Unauthorized", "Invalid token signature"),
AuthError::TokenExpired => ("Unauthorized", "Token has expired"),
AuthError::InvalidIssuer => ("Unauthorized", "Invalid token issuer"),
AuthError::InvalidAudience => ("Unauthorized", "Invalid token audience"),
AuthError::ProviderUnavailable(_) => ("ServiceUnavailable", "Auth provider unavailable"),
AuthError::Other(_) => ("Unauthorized", "Authentication error"),
};
HttpResponse::build(match status {
"Unauthorized" => actix_web::http::StatusCode::UNAUTHORIZED,
"Forbidden" => actix_web::http::StatusCode::FORBIDDEN,
"ServiceUnavailable" => actix_web::http::StatusCode::SERVICE_UNAVAILABLE,
_ => actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
})
.json(json!({
"error": status,
"message": message
}))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_check_resource_role() {
let claims = crate::auth::provider::Claims {
sub: "user-1".to_string(),
groups: vec![],
attributes: serde_json::Map::new(),
exp: 999999999,
iat: 0,
};
// User with no resource role defaults to Role::User
assert!(check_resource_role(&claims, "memory", "proj-1", Role::User));
assert!(!check_resource_role(&claims, "memory", "proj-1", Role::Viewer));
}
#[test]
fn test_check_group_membership() {
let claims = crate::auth::provider::Claims {
sub: "user-1".to_string(),
groups: vec!["admins".to_string(), "developers".to_string()],
attributes: serde_json::Map::new(),
exp: 999999999,
iat: 0,
};
assert!(check_group_membership(&claims, "admins"));
assert!(check_group_membership(&claims, "developers"));
assert!(!check_group_membership(&claims, "managers"));
}
#[test]
fn test_auth_error_response() {
let err = AuthError::MissingToken;
let response = auth_error_response(&err);
assert_eq!(response.status(), 401);
}
}