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

web.rs (5440B)


      1 use crate::{
      2     api::core::now,
      3     config::{self, Config},
      4     error::Error,
      5     util::{Cached, SafeString},
      6 };
      7 use rocket::{fs::NamedFile, http::ContentType, serde::json::Json, Catcher, Route};
      8 use serde_json::Value;
      9 use std::path::{Path, PathBuf};
     10 
     11 pub fn routes() -> Vec<Route> {
     12     // If adding more routes here, consider also adding them to
     13     // crate::utils::LOGGED_ROUTES to make sure they appear in the log
     14     let mut routes = routes![alive, alive_head, attachments, static_files];
     15     if config::get_config().web_vault_enabled {
     16         routes.append(&mut routes![app_id, web_files, web_index, web_index_head]);
     17     }
     18     routes
     19 }
     20 
     21 pub fn catchers() -> Vec<Catcher> {
     22     if config::get_config().web_vault_enabled {
     23         catchers![not_found]
     24     } else {
     25         Vec::new()
     26     }
     27 }
     28 
     29 #[catch(404)]
     30 const fn not_found() -> &'static str {
     31     "Web vault is permanently disabled."
     32 }
     33 
     34 #[get("/")]
     35 async fn web_index() -> Cached<Option<NamedFile>> {
     36     Cached::short(
     37         NamedFile::open(Path::new(Config::WEB_VAULT_FOLDER).join("index.html"))
     38             .await
     39             .ok(),
     40         false,
     41     )
     42 }
     43 #[head("/")]
     44 const fn web_index_head() {
     45     // Add an explicit HEAD route to prevent uptime monitoring services from
     46     // generating "No matching routes for HEAD /" error messages.
     47     //
     48     // Rocket automatically implements a HEAD route when there's a matching GET
     49     // route, but relying on this behavior also means a spurious error gets
     50     // logged due to <https://github.com/SergioBenitez/Rocket/issues/1098>.
     51 }
     52 
     53 #[get("/app-id.json")]
     54 fn app_id() -> Cached<(ContentType, Json<Value>)> {
     55     let content_type = ContentType::new("application", "fido.trusted-apps+json");
     56     Cached::long(
     57         (
     58             content_type,
     59             Json(json!({
     60             "trustedFacets": [
     61                 {
     62                 "version": { "major": 1i32, "minor": 0i32 },
     63                 "ids": [
     64                     // Per <https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-appid-and-facets-v2.0-id-20180227.html#determining-the-facetid-of-a-calling-application>:
     65                     //
     66                     // "In the Web case, the FacetID MUST be the Web Origin [RFC6454]
     67                     // of the web page triggering the FIDO operation, written as
     68                     // a URI with an empty path. Default ports are omitted and any
     69                     // path component is ignored."
     70                     //
     71                     // This leaves it unclear as to whether the path must be empty,
     72                     // or whether it can be non-empty and will be ignored. To be on
     73                     // the safe side, use a proper web origin (with empty path).
     74                     config::get_config().domain_origin(),
     75                     "ios:bundle-id:com.8bit.bitwarden",
     76                     "android:apk-key-hash:dUGFzUzf3lmHSLBDBIv+WaFyZMI" ]
     77                 }]
     78             })),
     79         ),
     80         true,
     81     )
     82 }
     83 
     84 #[get("/<p..>", rank = 10)] // Only match this if the other routes don't match
     85 async fn web_files(p: PathBuf) -> Cached<Option<NamedFile>> {
     86     Cached::long(
     87         NamedFile::open(Path::new(Config::WEB_VAULT_FOLDER).join(p))
     88             .await
     89             .ok(),
     90         true,
     91     )
     92 }
     93 
     94 #[allow(unused_variables, clippy::needless_pass_by_value)]
     95 #[get("/attachments/<uuid>/<file_id>?<token>")]
     96 fn attachments(uuid: SafeString, file_id: SafeString, token: &str) -> Option<NamedFile> {
     97     None
     98 }
     99 
    100 #[get("/alive")]
    101 fn alive() -> Json<String> {
    102     now()
    103 }
    104 #[head("/alive")]
    105 const fn alive_head() {}
    106 #[get("/vw_static/<filename>", rank = 2)]
    107 fn static_files(filename: &str) -> Result<(ContentType, &'static [u8]), Error> {
    108     match filename {
    109         "404.png" => Ok((ContentType::PNG, include_bytes!("../static/images/404.png"))),
    110         "logo-gray.png" => Ok((
    111             ContentType::PNG,
    112             include_bytes!("../static/images/logo-gray.png"),
    113         )),
    114         "error-x.svg" => Ok((
    115             ContentType::SVG,
    116             include_bytes!("../static/images/error-x.svg"),
    117         )),
    118         "vaultwarden-icon.png" => Ok((
    119             ContentType::PNG,
    120             include_bytes!("../static/images/vaultwarden-icon.png"),
    121         )),
    122         "vaultwarden-favicon.png" => Ok((
    123             ContentType::PNG,
    124             include_bytes!("../static/images/vaultwarden-favicon.png"),
    125         )),
    126         "404.css" => Ok((
    127             ContentType::CSS,
    128             include_bytes!("../static/scripts/404.css"),
    129         )),
    130         "bootstrap.css" => Ok((
    131             ContentType::CSS,
    132             include_bytes!("../static/scripts/bootstrap.css"),
    133         )),
    134         "bootstrap.bundle.js" => Ok((
    135             ContentType::JavaScript,
    136             include_bytes!("../static/scripts/bootstrap.bundle.js"),
    137         )),
    138         "jdenticon-3.3.0.js" => Ok((
    139             ContentType::JavaScript,
    140             include_bytes!("../static/scripts/jdenticon-3.3.0.js"),
    141         )),
    142         "datatables.js" => Ok((
    143             ContentType::JavaScript,
    144             include_bytes!("../static/scripts/datatables.js"),
    145         )),
    146         "datatables.css" => Ok((
    147             ContentType::CSS,
    148             include_bytes!("../static/scripts/datatables.css"),
    149         )),
    150         "jquery-3.7.1.slim.js" => Ok((
    151             ContentType::JavaScript,
    152             include_bytes!("../static/scripts/jquery-3.7.1.slim.js"),
    153         )),
    154         _ => err!(format!("Static file not found: {filename}")),
    155     }
    156 }