Release: NX9-Auth v0.3.0
This commit is contained in:
1 parent
6a04d7f793
commit
d93f2cef95
92 files changed
+2418
-1143
No files matched your search
+2
-2
@@ -6,8 +6,8 @@ use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use crate::{
|
||||
db::models::AuditLog,
|
||||
db::repository::audit::{self as audit_repo, AuditFilter},
|
||||
db::models::{AuditFilter, AuditLog},
|
||||
db::repository::audit as audit_repo,
|
||||
error::{AppError, Result},
|
||||
middleware::{auth::AuthUser, permissions::require},
|
||||
state::AppState,
|
||||
|
||||
+4
-1
@@ -112,7 +112,10 @@ pub async fn login(
|
||||
return Err(AppError::InvalidCredentials);
|
||||
}
|
||||
|
||||
let user = final_user.expect("authenticated user");
|
||||
let user = match final_user {
|
||||
Some(u) => u,
|
||||
None => return Err(AppError::InvalidCredentials),
|
||||
};
|
||||
|
||||
// Clear rate limit on success
|
||||
if let Some(ip_str) = &ctx.ip_address {
|
||||
|
||||
@@ -84,7 +84,7 @@ pub async fn dashboard(State(state): State<AppState>, auth: AuthUser) -> Result<
|
||||
let recent_personal = state
|
||||
.provider
|
||||
.audit()
|
||||
.list_filtered(&crate::db::repository::audit::AuditFilter {
|
||||
.list_filtered(&crate::db::models::AuditFilter {
|
||||
actor_user_id: Some(auth.user.id.clone()),
|
||||
limit: 10,
|
||||
..Default::default()
|
||||
@@ -175,7 +175,7 @@ pub async fn dashboard(State(state): State<AppState>, auth: AuthUser) -> Result<
|
||||
let recent_logins = state
|
||||
.provider
|
||||
.audit()
|
||||
.list_filtered(&crate::db::repository::audit::AuditFilter {
|
||||
.list_filtered(&crate::db::models::AuditFilter {
|
||||
action: Some("login_success".into()),
|
||||
limit: 10,
|
||||
..Default::default()
|
||||
|
||||
+21
-2
@@ -1,7 +1,26 @@
|
||||
use axum::Json;
|
||||
use axum::extract::State;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use crate::state::AppState;
|
||||
|
||||
/// GET /health
|
||||
pub async fn health() -> Json<Value> {
|
||||
Json(json!({ "status": "ok" }))
|
||||
pub async fn health(State(state): State<AppState>) -> Json<Value> {
|
||||
let backend = state
|
||||
.config
|
||||
.database
|
||||
.resolved_url()
|
||||
.map(|(_, b)| b.to_string())
|
||||
.unwrap_or_else(|_| "unknown".to_string());
|
||||
|
||||
let db_status = match state.provider.tenants().list().await {
|
||||
Ok(_) => "connected",
|
||||
Err(_) => "error",
|
||||
};
|
||||
|
||||
Json(json!({
|
||||
"status": if db_status == "connected" { "ok" } else { "degraded" },
|
||||
"db_backend": backend,
|
||||
"database_status": db_status
|
||||
}))
|
||||
}
|
||||
@@ -62,6 +62,28 @@ pub async fn serve_ui(uri: Uri) -> Response {
|
||||
return StatusCode::NOT_FOUND.into_response();
|
||||
}
|
||||
|
||||
// Security Hardening: Reject & sanitize any GET request containing credentials in query string.
|
||||
if let Some(query) = uri.query() {
|
||||
let q_lower = query.to_ascii_lowercase();
|
||||
if q_lower.contains("password=")
|
||||
|| q_lower.contains("username=")
|
||||
|| q_lower.contains("secret=")
|
||||
{
|
||||
tracing::warn!(path = %uri.path(), "rejected credential query parameters in GET request");
|
||||
let clean_path = if uri.path().is_empty() {
|
||||
"/"
|
||||
} else {
|
||||
uri.path()
|
||||
};
|
||||
return Response::builder()
|
||||
.status(StatusCode::SEE_OTHER)
|
||||
.header(header::LOCATION, clean_path)
|
||||
.header(header::CACHE_CONTROL, "no-store")
|
||||
.body(Body::empty())
|
||||
.unwrap_or_else(|_| StatusCode::BAD_REQUEST.into_response());
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize and reject path traversal
|
||||
if path.contains("..") {
|
||||
return StatusCode::BAD_REQUEST.into_response();
|
||||
|
||||
+13
-2
@@ -1,15 +1,26 @@
|
||||
use axum::Json;
|
||||
use axum::extract::State;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use crate::state::AppState;
|
||||
|
||||
/// GET /version
|
||||
///
|
||||
/// Returns build metadata baked in at compile time via `build.rs`.
|
||||
pub async fn version() -> Json<Value> {
|
||||
/// Returns build metadata baked in at compile time via `build.rs` and active db_backend.
|
||||
pub async fn version(State(state): State<AppState>) -> Json<Value> {
|
||||
let backend = state
|
||||
.config
|
||||
.database
|
||||
.resolved_url()
|
||||
.map(|(_, b)| b.to_string())
|
||||
.unwrap_or_else(|_| "unknown".to_string());
|
||||
|
||||
Json(json!({
|
||||
"name": env!("CARGO_PKG_NAME"),
|
||||
"version": env!("CARGO_PKG_VERSION"),
|
||||
"git_commit": env!("GIT_COMMIT"),
|
||||
"build_date": env!("BUILD_DATE"),
|
||||
"rust_version": env!("RUST_VERSION"),
|
||||
"db_backend": backend,
|
||||
}))
|
||||
}
|
||||
Reference in new issue
Block a user