Add QR codes, link expiry, password protection, previews, audit trail and bulk operations

This commit is contained in:
thakares committed 2026-06-12 19:57:53 +05:30
1 parent 157aa81252
commit bcbcc90d98
88 files changed
+5354 -815

No files matched your search

+1 -1
View File
@@ -1,4 +1,4 @@
use sha2::{Sha256, Digest};
use sha2::{Digest, Sha256};
// Deterministic CSRF token derived from session token
pub fn generate_csrf_token(session_id: &str) -> String {
+6 -5
View File
@@ -1,10 +1,10 @@
use crate::auth::session::authenticate_api_key;
use crate::models::User;
use crate::state::AppState;
use axum::{
extract::{FromRequestParts, FromRef},
extract::{FromRef, FromRequestParts},
http::{request::Parts, StatusCode},
};
use crate::state::AppState;
use crate::models::User;
use crate::auth::session::authenticate_api_key;
// Extractor: Authenticate API requests using Bearer token
pub struct ApiUser(pub User);
@@ -19,7 +19,8 @@ where
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
let app_state = AppState::from_ref(state);
let auth_header = parts.headers
let auth_header = parts
.headers
.get("Authorization")
.and_then(|h| h.to_str().ok())
.ok_or((StatusCode::UNAUTHORIZED, "Missing Authorization header"))?;
+4 -4
View File
@@ -1,9 +1,9 @@
pub mod password;
pub mod session;
pub mod csrf;
pub mod middleware;
pub mod password;
pub mod session;
pub use password::{hash_password, verify_password, verify_sha256};
pub use session::{generate_token, authenticate_session, authenticate_api_key};
pub use csrf::{generate_csrf_token, verify_csrf};
pub use middleware::ApiUser;
pub use password::{hash_password, verify_password, verify_sha256};
pub use session::{authenticate_api_key, authenticate_session, generate_token};
+7 -3
View File
@@ -1,21 +1,25 @@
use sha2::{Sha256, Digest};
use argon2::{
password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
Argon2,
};
use sha2::{Digest, Sha256};
// Hashing password with Argon2id
pub fn hash_password(password: &str) -> Result<String, argon2::password_hash::Error> {
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
let password_hash = argon2.hash_password(password.as_bytes(), &salt)?.to_string();
let password_hash = argon2
.hash_password(password.as_bytes(), &salt)?
.to_string();
Ok(password_hash)
}
// Verifying Argon2id password hash
pub fn verify_password(password: &str, hash: &str) -> bool {
if let Ok(parsed_hash) = PasswordHash::new(hash) {
Argon2::default().verify_password(password.as_bytes(), &parsed_hash).is_ok()
Argon2::default()
.verify_password(password.as_bytes(), &parsed_hash)
.is_ok()
} else {
false
}
+16 -14
View File
@@ -1,10 +1,12 @@
use sha2::{Sha256, Digest};
use rand::{RngCore, thread_rng};
use axum_extra::extract::CookieJar;
use rusqlite::Connection;
use chrono::Utc;
use crate::db::admin::{get_session, get_user_by_id, update_api_key_last_used, get_api_key_by_hash};
use crate::db::admin::{
get_api_key_by_hash, get_session, get_user_by_id, update_api_key_last_used,
};
use crate::models::User;
use axum_extra::extract::CookieJar;
use chrono::Utc;
use rand::{thread_rng, RngCore};
use rusqlite::Connection;
use sha2::{Digest, Sha256};
// Generate a secure random token (hex-encoded)
pub fn generate_token(bytes_len: usize) -> String {
@@ -22,13 +24,13 @@ pub fn authenticate_session(
Some(c) => c,
None => return Ok(None),
};
let session_id = cookie.value();
let session = match get_session(conn, session_id)? {
Some(s) => s,
None => return Ok(None),
};
// Check expiration
if let Ok(expires) = chrono::DateTime::parse_from_rfc3339(&session.expires_at) {
if expires.with_timezone(&Utc) < Utc::now() {
@@ -38,7 +40,7 @@ pub fn authenticate_session(
} else {
return Ok(None);
}
// Get user
if let Some(user) = get_user_by_id(conn, &session.user_id)? {
Ok(Some((user, session.id)))
@@ -55,26 +57,26 @@ pub fn authenticate_api_key(
if !auth_header.starts_with("Bearer ") {
return Ok(None);
}
let key = auth_header.trim_start_matches("Bearer ").trim();
if key.is_empty() {
return Ok(None);
}
// Hash the API key using SHA-256 to compare with stored hash
let mut hasher = Sha256::new();
hasher.update(key.as_bytes());
let hashed_key = hex::encode(hasher.finalize());
if let Some(api_key_rec) = get_api_key_by_hash(conn, &hashed_key)? {
// Update last used timestamp
update_api_key_last_used(conn, &api_key_rec.id)?;
// Get user
if let Some(user) = get_user_by_id(conn, &api_key_rec.user_id)? {
return Ok(Some(user));
}
}
Ok(None)
}