vw_small

Hardened fork of Vaultwarden (https://github.com/dani-garcia/vaultwarden) with fewer features.
git clone https://git.philomathiclife.com/repos/vw_small
Log | Files | Refs | README

commit 2cd17fe7afeaef2a29787999b1cb48a512811571
parent f44b2611e6f24c8c30f3ef0f239e18244b7376c2
Author: Daniel GarcĂ­a <dani-garcia@users.noreply.github.com>
Date:   Fri, 25 Jun 2021 20:33:51 +0200

Add token with short expiration time to send url

Diffstat:
Msrc/api/core/sends.rs | 27++++++++++++++++++++++++---
Msrc/api/web.rs | 7+------
Msrc/auth.rs | 61+++++++++++++++++++++++++++----------------------------------
3 files changed, 52 insertions(+), 43 deletions(-)

diff --git a/src/api/core/sends.rs b/src/api/core/sends.rs @@ -2,7 +2,7 @@ use std::{io::Read, path::Path}; use chrono::{DateTime, Duration, Utc}; use multipart::server::{save::SavedData, Multipart, SaveResult}; -use rocket::{http::ContentType, Data}; +use rocket::{http::ContentType, response::NamedFile, Data}; use rocket_contrib::json::Json; use serde_json::Value; @@ -16,7 +16,16 @@ use crate::{ const SEND_INACCESSIBLE_MSG: &str = "Send does not exist or is no longer available"; pub fn routes() -> Vec<rocket::Route> { - routes![post_send, post_send_file, post_access, post_access_file, put_send, delete_send, put_remove_password] + routes![ + post_send, + post_send_file, + post_access, + post_access_file, + put_send, + delete_send, + put_remove_password, + download_send + ] } pub fn purge_sends(pool: DbPool) { @@ -316,13 +325,25 @@ fn post_access_file( send.save(&conn)?; + let token_claims = crate::auth::generate_send_claims(&send_id, &file_id); + let token = crate::auth::encode_jwt(&token_claims); Ok(Json(json!({ "Object": "send-fileDownload", "Id": file_id, - "Url": format!("{}/sends/{}/{}", &host.host, send_id, file_id) + "Url": format!("{}/api/sends/{}/{}?t={}", &host.host, send_id, file_id, token) }))) } +#[get("/sends/<send_id>/<file_id>?<t>")] +fn download_send(send_id: String, file_id: String, t: String) -> Option<NamedFile> { + if let Ok(claims) = crate::auth::decode_send(&t) { + if claims.sub == format!("{}/{}", send_id, file_id) { + return NamedFile::open(Path::new(&CONFIG.sends_folder()).join(send_id).join(file_id)).ok(); + } + } + None +} + #[put("/sends/<id>", data = "<data>")] fn put_send(id: String, data: JsonUpcase<SendData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult { enforce_disable_send_policy(&headers, &conn)?; diff --git a/src/api/web.rs b/src/api/web.rs @@ -10,7 +10,7 @@ pub fn routes() -> Vec<Route> { // If addding more routes here, consider also adding them to // crate::utils::LOGGED_ROUTES to make sure they appear in the log if CONFIG.web_vault_enabled() { - routes![web_index, app_id, web_files, attachments, sends, alive, static_files] + routes![web_index, app_id, web_files, attachments, alive, static_files] } else { routes![attachments, alive, static_files] } @@ -60,11 +60,6 @@ fn attachments(uuid: String, file_id: String) -> Option<NamedFile> { NamedFile::open(Path::new(&CONFIG.attachments_folder()).join(uuid).join(file_id)).ok() } -#[get("/sends/<send_id>/<file_id>")] -fn sends(send_id: String, file_id: String) -> Option<NamedFile> { - NamedFile::open(Path::new(&CONFIG.sends_folder()).join(send_id).join(file_id)).ok() -} - #[get("/alive")] fn alive() -> Json<String> { use crate::util::format_date; diff --git a/src/auth.rs b/src/auth.rs @@ -19,11 +19,14 @@ const JWT_ALGORITHM: Algorithm = Algorithm::RS256; pub static DEFAULT_VALIDITY: Lazy<Duration> = Lazy::new(|| Duration::hours(2)); static JWT_HEADER: Lazy<Header> = Lazy::new(|| Header::new(JWT_ALGORITHM)); + pub static JWT_LOGIN_ISSUER: Lazy<String> = Lazy::new(|| format!("{}|login", CONFIG.domain_origin())); static JWT_INVITE_ISSUER: Lazy<String> = Lazy::new(|| format!("{}|invite", CONFIG.domain_origin())); static JWT_DELETE_ISSUER: Lazy<String> = Lazy::new(|| format!("{}|delete", CONFIG.domain_origin())); static JWT_VERIFYEMAIL_ISSUER: Lazy<String> = Lazy::new(|| format!("{}|verifyemail", CONFIG.domain_origin())); static JWT_ADMIN_ISSUER: Lazy<String> = Lazy::new(|| format!("{}|admin", CONFIG.domain_origin())); +static JWT_SEND_ISSUER: Lazy<String> = Lazy::new(|| format!("{}|send", CONFIG.domain_origin())); + static PRIVATE_RSA_KEY: Lazy<Vec<u8>> = Lazy::new(|| match read_file(&CONFIG.private_rsa_key()) { Ok(key) => key, Err(e) => panic!("Error loading private RSA Key.\n Error: {}", e), @@ -66,18 +69,22 @@ pub fn decode_invite(token: &str) -> Result<InviteJwtClaims, Error> { decode_jwt(token, JWT_INVITE_ISSUER.to_string()) } -pub fn decode_delete(token: &str) -> Result<DeleteJwtClaims, Error> { +pub fn decode_delete(token: &str) -> Result<BasicJwtClaims, Error> { decode_jwt(token, JWT_DELETE_ISSUER.to_string()) } -pub fn decode_verify_email(token: &str) -> Result<VerifyEmailJwtClaims, Error> { +pub fn decode_verify_email(token: &str) -> Result<BasicJwtClaims, Error> { decode_jwt(token, JWT_VERIFYEMAIL_ISSUER.to_string()) } -pub fn decode_admin(token: &str) -> Result<AdminJwtClaims, Error> { +pub fn decode_admin(token: &str) -> Result<BasicJwtClaims, Error> { decode_jwt(token, JWT_ADMIN_ISSUER.to_string()) } +pub fn decode_send(token: &str) -> Result<BasicJwtClaims, Error> { + decode_jwt(token, JWT_SEND_ISSUER.to_string()) +} + #[derive(Debug, Serialize, Deserialize)] pub struct LoginJwtClaims { // Not before @@ -147,7 +154,7 @@ pub fn generate_invite_claims( } #[derive(Debug, Serialize, Deserialize)] -pub struct DeleteJwtClaims { +pub struct BasicJwtClaims { // Not before pub nbf: i64, // Expiration time @@ -158,9 +165,9 @@ pub struct DeleteJwtClaims { pub sub: String, } -pub fn generate_delete_claims(uuid: String) -> DeleteJwtClaims { +pub fn generate_delete_claims(uuid: String) -> BasicJwtClaims { let time_now = Utc::now().naive_utc(); - DeleteJwtClaims { + BasicJwtClaims { nbf: time_now.timestamp(), exp: (time_now + Duration::days(5)).timestamp(), iss: JWT_DELETE_ISSUER.to_string(), @@ -168,21 +175,9 @@ pub fn generate_delete_claims(uuid: String) -> DeleteJwtClaims { } } -#[derive(Debug, Serialize, Deserialize)] -pub struct VerifyEmailJwtClaims { - // Not before - pub nbf: i64, - // Expiration time - pub exp: i64, - // Issuer - pub iss: String, - // Subject - pub sub: String, -} - -pub fn generate_verify_email_claims(uuid: String) -> DeleteJwtClaims { +pub fn generate_verify_email_claims(uuid: String) -> BasicJwtClaims { let time_now = Utc::now().naive_utc(); - DeleteJwtClaims { + BasicJwtClaims { nbf: time_now.timestamp(), exp: (time_now + Duration::days(5)).timestamp(), iss: JWT_VERIFYEMAIL_ISSUER.to_string(), @@ -190,21 +185,9 @@ pub fn generate_verify_email_claims(uuid: String) -> DeleteJwtClaims { } } -#[derive(Debug, Serialize, Deserialize)] -pub struct AdminJwtClaims { - // Not before - pub nbf: i64, - // Expiration time - pub exp: i64, - // Issuer - pub iss: String, - // Subject - pub sub: String, -} - -pub fn generate_admin_claims() -> AdminJwtClaims { +pub fn generate_admin_claims() -> BasicJwtClaims { let time_now = Utc::now().naive_utc(); - AdminJwtClaims { + BasicJwtClaims { nbf: time_now.timestamp(), exp: (time_now + Duration::minutes(20)).timestamp(), iss: JWT_ADMIN_ISSUER.to_string(), @@ -212,6 +195,16 @@ pub fn generate_admin_claims() -> AdminJwtClaims { } } +pub fn generate_send_claims(send_id: &str, file_id: &str) -> BasicJwtClaims { + let time_now = Utc::now().naive_utc(); + BasicJwtClaims { + nbf: time_now.timestamp(), + exp: (time_now + Duration::minutes(2)).timestamp(), + iss: JWT_SEND_ISSUER.to_string(), + sub: format!("{}/{}", send_id, file_id), + } +} + // // Bearer token authentication //