geforkt von mirrored/vaultwarden
Add backend support for alternate base dir (subdir/subpath) hosting
To use this, include a path in the `DOMAIN` URL, e.g.: * `DOMAIN=https://example.com/custom-path` * `DOMAIN=https://example.com/multiple/levels/are/ok`
Dieser Commit ist enthalten in:
Ursprung
63459c5f72
Commit
29a0795219
21 geänderte Dateien mit 95 neuen und 53 gelöschten Zeilen
|
@ -52,6 +52,10 @@ const ADMIN_PATH: &str = "/admin";
|
||||||
const BASE_TEMPLATE: &str = "admin/base";
|
const BASE_TEMPLATE: &str = "admin/base";
|
||||||
const VERSION: Option<&str> = option_env!("GIT_VERSION");
|
const VERSION: Option<&str> = option_env!("GIT_VERSION");
|
||||||
|
|
||||||
|
fn admin_path() -> String {
|
||||||
|
format!("{}{}", CONFIG.domain_path(), ADMIN_PATH)
|
||||||
|
}
|
||||||
|
|
||||||
#[get("/", rank = 2)]
|
#[get("/", rank = 2)]
|
||||||
fn admin_login(flash: Option<FlashMessage>) -> ApiResult<Html<String>> {
|
fn admin_login(flash: Option<FlashMessage>) -> ApiResult<Html<String>> {
|
||||||
// If there is an error, show it
|
// If there is an error, show it
|
||||||
|
@ -76,7 +80,7 @@ fn post_admin_login(data: Form<LoginForm>, mut cookies: Cookies, ip: ClientIp) -
|
||||||
if !_validate_token(&data.token) {
|
if !_validate_token(&data.token) {
|
||||||
error!("Invalid admin token. IP: {}", ip.ip);
|
error!("Invalid admin token. IP: {}", ip.ip);
|
||||||
Err(Flash::error(
|
Err(Flash::error(
|
||||||
Redirect::to(ADMIN_PATH),
|
Redirect::to(admin_path()),
|
||||||
"Invalid admin token, please try again.",
|
"Invalid admin token, please try again.",
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
|
@ -85,14 +89,14 @@ fn post_admin_login(data: Form<LoginForm>, mut cookies: Cookies, ip: ClientIp) -
|
||||||
let jwt = encode_jwt(&claims);
|
let jwt = encode_jwt(&claims);
|
||||||
|
|
||||||
let cookie = Cookie::build(COOKIE_NAME, jwt)
|
let cookie = Cookie::build(COOKIE_NAME, jwt)
|
||||||
.path(ADMIN_PATH)
|
.path(admin_path())
|
||||||
.max_age(chrono::Duration::minutes(20))
|
.max_age(chrono::Duration::minutes(20))
|
||||||
.same_site(SameSite::Strict)
|
.same_site(SameSite::Strict)
|
||||||
.http_only(true)
|
.http_only(true)
|
||||||
.finish();
|
.finish();
|
||||||
|
|
||||||
cookies.add(cookie);
|
cookies.add(cookie);
|
||||||
Ok(Redirect::to(ADMIN_PATH))
|
Ok(Redirect::to(admin_path()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,7 +171,7 @@ fn invite_user(data: Json<InviteData>, _token: AdminToken, conn: DbConn) -> Empt
|
||||||
#[get("/logout")]
|
#[get("/logout")]
|
||||||
fn logout(mut cookies: Cookies) -> Result<Redirect, ()> {
|
fn logout(mut cookies: Cookies) -> Result<Redirect, ()> {
|
||||||
cookies.remove(Cookie::named(COOKIE_NAME));
|
cookies.remove(Cookie::named(COOKIE_NAME));
|
||||||
Ok(Redirect::to(ADMIN_PATH))
|
Ok(Redirect::to(admin_path()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/users")]
|
#[get("/users")]
|
||||||
|
|
|
@ -172,7 +172,7 @@ fn hibp_breach(username: String) -> JsonResult {
|
||||||
"BreachDate": "2019-08-18T00:00:00Z",
|
"BreachDate": "2019-08-18T00:00:00Z",
|
||||||
"AddedDate": "2019-08-18T00:00:00Z",
|
"AddedDate": "2019-08-18T00:00:00Z",
|
||||||
"Description": format!("Go to: <a href=\"https://haveibeenpwned.com/account/{account}\" target=\"_blank\" rel=\"noopener\">https://haveibeenpwned.com/account/{account}</a> for a manual check.<br/><br/>HaveIBeenPwned API key not set!<br/>Go to <a href=\"https://haveibeenpwned.com/API/Key\" target=\"_blank\" rel=\"noopener\">https://haveibeenpwned.com/API/Key</a> to purchase an API key from HaveIBeenPwned.<br/><br/>", account=username),
|
"Description": format!("Go to: <a href=\"https://haveibeenpwned.com/account/{account}\" target=\"_blank\" rel=\"noopener\">https://haveibeenpwned.com/account/{account}</a> for a manual check.<br/><br/>HaveIBeenPwned API key not set!<br/>Go to <a href=\"https://haveibeenpwned.com/API/Key\" target=\"_blank\" rel=\"noopener\">https://haveibeenpwned.com/API/Key</a> to purchase an API key from HaveIBeenPwned.<br/><br/>", account=username),
|
||||||
"LogoPath": "/bwrs_static/hibp.png",
|
"LogoPath": "bwrs_static/hibp.png",
|
||||||
"PwnCount": 0,
|
"PwnCount": 0,
|
||||||
"DataClasses": [
|
"DataClasses": [
|
||||||
"Error - No API key set!"
|
"Error - No API key set!"
|
||||||
|
|
|
@ -37,7 +37,17 @@ fn app_id() -> Cached<Content<Json<Value>>> {
|
||||||
{
|
{
|
||||||
"version": { "major": 1, "minor": 0 },
|
"version": { "major": 1, "minor": 0 },
|
||||||
"ids": [
|
"ids": [
|
||||||
&CONFIG.domain(),
|
// 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>:
|
||||||
|
//
|
||||||
|
// "In the Web case, the FacetID MUST be the Web Origin [RFC6454]
|
||||||
|
// of the web page triggering the FIDO operation, written as
|
||||||
|
// a URI with an empty path. Default ports are omitted and any
|
||||||
|
// path component is ignored."
|
||||||
|
//
|
||||||
|
// This leaves it unclear as to whether the path must be empty,
|
||||||
|
// or whether it can be non-empty and will be ignored. To be on
|
||||||
|
// the safe side, use a proper web origin (with empty path).
|
||||||
|
&CONFIG.domain_origin(),
|
||||||
"ios:bundle-id:com.8bit.bitwarden",
|
"ios:bundle-id:com.8bit.bitwarden",
|
||||||
"android:apk-key-hash:dUGFzUzf3lmHSLBDBIv+WaFyZMI" ]
|
"android:apk-key-hash:dUGFzUzf3lmHSLBDBIv+WaFyZMI" ]
|
||||||
}]
|
}]
|
||||||
|
@ -75,6 +85,6 @@ fn static_files(filename: String) -> Result<Content<&'static [u8]>, Error> {
|
||||||
"bootstrap-native-v4.js" => Ok(Content(ContentType::JavaScript, include_bytes!("../static/scripts/bootstrap-native-v4.js"))),
|
"bootstrap-native-v4.js" => Ok(Content(ContentType::JavaScript, include_bytes!("../static/scripts/bootstrap-native-v4.js"))),
|
||||||
"md5.js" => Ok(Content(ContentType::JavaScript, include_bytes!("../static/scripts/md5.js"))),
|
"md5.js" => Ok(Content(ContentType::JavaScript, include_bytes!("../static/scripts/md5.js"))),
|
||||||
"identicon.js" => Ok(Content(ContentType::JavaScript, include_bytes!("../static/scripts/identicon.js"))),
|
"identicon.js" => Ok(Content(ContentType::JavaScript, include_bytes!("../static/scripts/identicon.js"))),
|
||||||
_ => err!("Image not found"),
|
_ => err!(format!("Static file not found: {}", filename)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
10
src/auth.rs
10
src/auth.rs
|
@ -16,11 +16,11 @@ const JWT_ALGORITHM: Algorithm = Algorithm::RS256;
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref DEFAULT_VALIDITY: Duration = Duration::hours(2);
|
pub static ref DEFAULT_VALIDITY: Duration = Duration::hours(2);
|
||||||
static ref JWT_HEADER: Header = Header::new(JWT_ALGORITHM);
|
static ref JWT_HEADER: Header = Header::new(JWT_ALGORITHM);
|
||||||
pub static ref JWT_LOGIN_ISSUER: String = format!("{}|login", CONFIG.domain());
|
pub static ref JWT_LOGIN_ISSUER: String = format!("{}|login", CONFIG.domain_origin());
|
||||||
pub static ref JWT_INVITE_ISSUER: String = format!("{}|invite", CONFIG.domain());
|
pub static ref JWT_INVITE_ISSUER: String = format!("{}|invite", CONFIG.domain_origin());
|
||||||
pub static ref JWT_DELETE_ISSUER: String = format!("{}|delete", CONFIG.domain());
|
pub static ref JWT_DELETE_ISSUER: String = format!("{}|delete", CONFIG.domain_origin());
|
||||||
pub static ref JWT_VERIFYEMAIL_ISSUER: String = format!("{}|verifyemail", CONFIG.domain());
|
pub static ref JWT_VERIFYEMAIL_ISSUER: String = format!("{}|verifyemail", CONFIG.domain_origin());
|
||||||
pub static ref JWT_ADMIN_ISSUER: String = format!("{}|admin", CONFIG.domain());
|
pub static ref JWT_ADMIN_ISSUER: String = format!("{}|admin", CONFIG.domain_origin());
|
||||||
static ref PRIVATE_RSA_KEY: Vec<u8> = match read_file(&CONFIG.private_rsa_key()) {
|
static ref PRIVATE_RSA_KEY: Vec<u8> = match read_file(&CONFIG.private_rsa_key()) {
|
||||||
Ok(key) => key,
|
Ok(key) => key,
|
||||||
Err(e) => panic!("Error loading private RSA Key.\n Error: {}", e),
|
Err(e) => panic!("Error loading private RSA Key.\n Error: {}", e),
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
|
|
||||||
|
use reqwest::Url;
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::util::{get_env, get_env_bool};
|
use crate::util::{get_env, get_env_bool};
|
||||||
|
|
||||||
|
@ -240,6 +242,10 @@ make_config! {
|
||||||
domain: String, true, def, "http://localhost".to_string();
|
domain: String, true, def, "http://localhost".to_string();
|
||||||
/// Domain Set |> Indicates if the domain is set by the admin. Otherwise the default will be used.
|
/// Domain Set |> Indicates if the domain is set by the admin. Otherwise the default will be used.
|
||||||
domain_set: bool, false, def, false;
|
domain_set: bool, false, def, false;
|
||||||
|
/// Domain origin |> Domain URL origin (in https://example.com:8443/path, https://example.com:8443 is the origin)
|
||||||
|
domain_origin: String, false, auto, |c| extract_url_origin(&c.domain);
|
||||||
|
/// Domain path |> Domain URL path (in https://example.com:8443/path, /path is the path)
|
||||||
|
domain_path: String, false, auto, |c| extract_url_path(&c.domain);
|
||||||
/// Enable web vault
|
/// Enable web vault
|
||||||
web_vault_enabled: bool, false, def, true;
|
web_vault_enabled: bool, false, def, true;
|
||||||
|
|
||||||
|
@ -457,6 +463,21 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extracts an RFC 6454 web origin from a URL.
|
||||||
|
fn extract_url_origin(url: &str) -> String {
|
||||||
|
let url = Url::parse(url).expect("valid URL");
|
||||||
|
|
||||||
|
url.origin().ascii_serialization()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extracts the path from a URL.
|
||||||
|
/// All trailing '/' chars are trimmed, even if the path is a lone '/'.
|
||||||
|
fn extract_url_path(url: &str) -> String {
|
||||||
|
let url = Url::parse(url).expect("valid URL");
|
||||||
|
|
||||||
|
url.path().trim_end_matches('/').to_string()
|
||||||
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn load() -> Result<Self, Error> {
|
pub fn load() -> Result<Self, Error> {
|
||||||
// Loading from env and file
|
// Loading from env and file
|
||||||
|
|
18
src/main.rs
18
src/main.rs
|
@ -255,18 +255,20 @@ mod migrations {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn launch_rocket(extra_debug: bool) {
|
fn launch_rocket(extra_debug: bool) {
|
||||||
// Create Rocket object, this stores current log level and sets it's own
|
// Create Rocket object, this stores current log level and sets its own
|
||||||
let rocket = rocket::ignite();
|
let rocket = rocket::ignite();
|
||||||
|
|
||||||
// If addding more base paths here, consider also adding them to
|
let basepath = &CONFIG.domain_path();
|
||||||
|
|
||||||
|
// If adding more paths here, consider also adding them to
|
||||||
// crate::utils::LOGGED_ROUTES to make sure they appear in the log
|
// crate::utils::LOGGED_ROUTES to make sure they appear in the log
|
||||||
let rocket = rocket
|
let rocket = rocket
|
||||||
.mount("/", api::web_routes())
|
.mount(&[basepath, "/"].concat(), api::web_routes())
|
||||||
.mount("/api", api::core_routes())
|
.mount(&[basepath, "/api"].concat(), api::core_routes())
|
||||||
.mount("/admin", api::admin_routes())
|
.mount(&[basepath, "/admin"].concat(), api::admin_routes())
|
||||||
.mount("/identity", api::identity_routes())
|
.mount(&[basepath, "/identity"].concat(), api::identity_routes())
|
||||||
.mount("/icons", api::icons_routes())
|
.mount(&[basepath, "/icons"].concat(), api::icons_routes())
|
||||||
.mount("/notifications", api::notifications_routes())
|
.mount(&[basepath, "/notifications"].concat(), api::notifications_routes())
|
||||||
.manage(db::init_pool())
|
.manage(db::init_pool())
|
||||||
.manage(api::start_notification_server())
|
.manage(api::start_notification_server())
|
||||||
.attach(util::AppHeaders())
|
.attach(util::AppHeaders())
|
||||||
|
|
|
@ -6,10 +6,10 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<title>Bitwarden_rs Admin Panel</title>
|
<title>Bitwarden_rs Admin Panel</title>
|
||||||
|
|
||||||
<link rel="stylesheet" href="/bwrs_static/bootstrap.css" />
|
<link rel="stylesheet" href="bwrs_static/bootstrap.css" />
|
||||||
<script src="/bwrs_static/bootstrap-native-v4.js"></script>
|
<script src="bwrs_static/bootstrap-native-v4.js"></script>
|
||||||
<script src="/bwrs_static/md5.js"></script>
|
<script src="bwrs_static/md5.js"></script>
|
||||||
<script src="/bwrs_static/identicon.js"></script>
|
<script src="bwrs_static/identicon.js"></script>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
padding-top: 70px;
|
padding-top: 70px;
|
||||||
|
@ -38,10 +38,10 @@
|
||||||
<div class="navbar-collapse">
|
<div class="navbar-collapse">
|
||||||
<ul class="navbar-nav">
|
<ul class="navbar-nav">
|
||||||
<li class="nav-item active">
|
<li class="nav-item active">
|
||||||
<a class="nav-link" href="/admin">Admin Panel</a>
|
<a class="nav-link" href="admin">Admin Panel</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/">Vault</a>
|
<a class="nav-link" href=".">Vault</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -55,7 +55,7 @@
|
||||||
|
|
||||||
{{#if logged_in}}
|
{{#if logged_in}}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/admin/logout">Log Out</a>
|
<a class="nav-link" href="admin/logout">Log Out</a>
|
||||||
</li>
|
</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -64,4 +64,4 @@
|
||||||
{{> (page_content) }}
|
{{> (page_content) }}
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -225,7 +225,7 @@
|
||||||
var input_mail = prompt("To delete user '" + mail + "', please type the email below")
|
var input_mail = prompt("To delete user '" + mail + "', please type the email below")
|
||||||
if (input_mail != null) {
|
if (input_mail != null) {
|
||||||
if (input_mail == mail) {
|
if (input_mail == mail) {
|
||||||
_post("/admin/users/" + id + "/delete",
|
_post("admin/users/" + id + "/delete",
|
||||||
"User deleted correctly",
|
"User deleted correctly",
|
||||||
"Error deleting user");
|
"Error deleting user");
|
||||||
} else {
|
} else {
|
||||||
|
@ -235,19 +235,19 @@
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
function remove2fa(id) {
|
function remove2fa(id) {
|
||||||
_post("/admin/users/" + id + "/remove-2fa",
|
_post("admin/users/" + id + "/remove-2fa",
|
||||||
"2FA removed correctly",
|
"2FA removed correctly",
|
||||||
"Error removing 2FA");
|
"Error removing 2FA");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
function deauthUser(id) {
|
function deauthUser(id) {
|
||||||
_post("/admin/users/" + id + "/deauth",
|
_post("admin/users/" + id + "/deauth",
|
||||||
"Sessions deauthorized correctly",
|
"Sessions deauthorized correctly",
|
||||||
"Error deauthorizing sessions");
|
"Error deauthorizing sessions");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
function updateRevisions() {
|
function updateRevisions() {
|
||||||
_post("/admin/users/update_revision",
|
_post("admin/users/update_revision",
|
||||||
"Success, clients will sync next time they connect",
|
"Success, clients will sync next time they connect",
|
||||||
"Error forcing clients to sync");
|
"Error forcing clients to sync");
|
||||||
return false;
|
return false;
|
||||||
|
@ -256,7 +256,7 @@
|
||||||
inv = document.getElementById("email-invite");
|
inv = document.getElementById("email-invite");
|
||||||
data = JSON.stringify({ "email": inv.value });
|
data = JSON.stringify({ "email": inv.value });
|
||||||
inv.value = "";
|
inv.value = "";
|
||||||
_post("/admin/invite/", "User invited correctly",
|
_post("admin/invite/", "User invited correctly",
|
||||||
"Error inviting user", data);
|
"Error inviting user", data);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -278,7 +278,7 @@
|
||||||
}
|
}
|
||||||
function saveConfig() {
|
function saveConfig() {
|
||||||
data = JSON.stringify(getFormData());
|
data = JSON.stringify(getFormData());
|
||||||
_post("/admin/config/", "Config saved correctly",
|
_post("admin/config/", "Config saved correctly",
|
||||||
"Error saving config", data);
|
"Error saving config", data);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -286,7 +286,7 @@
|
||||||
var input = prompt("This will remove all user configurations, and restore the defaults and the " +
|
var input = prompt("This will remove all user configurations, and restore the defaults and the " +
|
||||||
"values set by the environment. This operation could be dangerous. Type 'DELETE' to proceed:");
|
"values set by the environment. This operation could be dangerous. Type 'DELETE' to proceed:");
|
||||||
if (input === "DELETE") {
|
if (input === "DELETE") {
|
||||||
_post("/admin/config/delete",
|
_post("admin/config/delete",
|
||||||
"Config deleted correctly",
|
"Config deleted correctly",
|
||||||
"Error deleting config");
|
"Error deleting config");
|
||||||
} else {
|
} else {
|
||||||
|
@ -296,7 +296,7 @@
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
function backupDatabase() {
|
function backupDatabase() {
|
||||||
_post("/admin/config/backup_db",
|
_post("admin/config/backup_db",
|
||||||
"Backup created successfully",
|
"Backup created successfully",
|
||||||
"Error creating backup");
|
"Error creating backup");
|
||||||
return false;
|
return false;
|
||||||
|
@ -336,4 +336,4 @@
|
||||||
// {{#each config}} {{#if grouptoggle}}
|
// {{#each config}} {{#if grouptoggle}}
|
||||||
masterCheck("input_{{grouptoggle}}", "#g_{{group}} input");
|
masterCheck("input_{{grouptoggle}}", "#g_{{group}} input");
|
||||||
// {{/if}} {{/each}}
|
// {{/if}} {{/each}}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -3,6 +3,6 @@ Invitation accepted
|
||||||
<html>
|
<html>
|
||||||
<p>
|
<p>
|
||||||
Your invitation for <b>{{email}}</b> to join <b>{{org_name}}</b> was accepted.
|
Your invitation for <b>{{email}}</b> to join <b>{{org_name}}</b> was accepted.
|
||||||
Please <a href="{{url}}">log in</a> to the bitwarden_rs server and confirm them from the organization management page.
|
Please <a href="{{url}}/">log in</a> to the bitwarden_rs server and confirm them from the organization management page.
|
||||||
</p>
|
</p>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -101,7 +101,7 @@ Invitation accepted
|
||||||
</tr>
|
</tr>
|
||||||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none;" valign="top">
|
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none;" valign="top">
|
||||||
Please <a href="{{url}}">log in</a> to the bitwarden_rs server and confirm them from the organization management page.
|
Please <a href="{{url}}/">log in</a> to the bitwarden_rs server and confirm them from the organization management page.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
|
|
|
@ -3,6 +3,6 @@ Invitation to {{org_name}} confirmed
|
||||||
<html>
|
<html>
|
||||||
<p>
|
<p>
|
||||||
Your invitation to join <b>{{org_name}}</b> was confirmed.
|
Your invitation to join <b>{{org_name}}</b> was confirmed.
|
||||||
It will now appear under the Organizations the next time you <a href="{{url}}">log in</a> to the web vault.
|
It will now appear under the Organizations the next time you <a href="{{url}}/">log in</a> to the web vault.
|
||||||
</p>
|
</p>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -102,7 +102,7 @@ Invitation to {{org_name}} confirmed
|
||||||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
<td class="content-block last" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0; -webkit-text-size-adjust: none;" valign="top">
|
<td class="content-block last" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0; -webkit-text-size-adjust: none;" valign="top">
|
||||||
Any collections and logins being shared with you by this organization will now appear in your Bitwarden vault. <br>
|
Any collections and logins being shared with you by this organization will now appear in your Bitwarden vault. <br>
|
||||||
<a href="{{url}}">Log in</a>
|
<a href="{{url}}/">Log in</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -9,6 +9,6 @@ New Device Logged In From {{device}}
|
||||||
Device Type: {{device}}
|
Device Type: {{device}}
|
||||||
|
|
||||||
You can deauthorize all devices that have access to your account from the
|
You can deauthorize all devices that have access to your account from the
|
||||||
<a href="{{url}}">web vault</a> under Settings > My Account > Deauthorize Sessions.
|
<a href="{{url}}/">web vault</a> under Settings > My Account > Deauthorize Sessions.
|
||||||
</p>
|
</p>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -116,7 +116,7 @@ New Device Logged In From {{device}}
|
||||||
</tr>
|
</tr>
|
||||||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
<td class="content-block last" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0; -webkit-text-size-adjust: none;" valign="top">
|
<td class="content-block last" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0; -webkit-text-size-adjust: none;" valign="top">
|
||||||
You can deauthorize all devices that have access to your account from the <a href="{{url}}">web vault</a> under Settings > My Account > Deauthorize Sessions.
|
You can deauthorize all devices that have access to your account from the <a href="{{url}}/">web vault</a> under Settings > My Account > Deauthorize Sessions.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -3,7 +3,7 @@ Your master password hint
|
||||||
You (or someone) recently requested your master password hint.
|
You (or someone) recently requested your master password hint.
|
||||||
|
|
||||||
Your hint is: "{{hint}}"
|
Your hint is: "{{hint}}"
|
||||||
Log in: <a href="{{url}}">Web Vault</a>
|
Log in: <a href="{{url}}/">Web Vault</a>
|
||||||
|
|
||||||
If you cannot remember your master password, there is no way to recover your data. The only option to gain access to your account again is to <a href="{{url}}/#/recover-delete">delete the account</a> so that you can register again and start over. All data associated with your account will be deleted.
|
If you cannot remember your master password, there is no way to recover your data. The only option to gain access to your account again is to <a href="{{url}}/#/recover-delete">delete the account</a> so that you can register again and start over. All data associated with your account will be deleted.
|
||||||
|
|
||||||
|
|
|
@ -102,7 +102,7 @@ Your master password hint
|
||||||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none;" valign="top">
|
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none;" valign="top">
|
||||||
Your hint is: "{{hint}}"<br style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;" />
|
Your hint is: "{{hint}}"<br style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;" />
|
||||||
Log in: <a href="{{url}}">Web Vault</a>
|
Log in: <a href="{{url}}/">Web Vault</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
|
|
|
@ -2,7 +2,7 @@ Welcome
|
||||||
<!---------------->
|
<!---------------->
|
||||||
<html>
|
<html>
|
||||||
<p>
|
<p>
|
||||||
Thank you for creating an account at <a href="{{url}}">{{url}}</a>. You may now log in with your new account.
|
Thank you for creating an account at <a href="{{url}}/">{{url}}</a>. You may now log in with your new account.
|
||||||
</p>
|
</p>
|
||||||
<p>If you did not request to create an account, you can safely ignore this email.</p>
|
<p>If you did not request to create an account, you can safely ignore this email.</p>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -96,7 +96,7 @@ Welcome
|
||||||
<table width="100%" cellpadding="0" cellspacing="0" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<table width="100%" cellpadding="0" cellspacing="0" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none; text-align: center;" valign="top" align="center">
|
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none; text-align: center;" valign="top" align="center">
|
||||||
Thank you for creating an account at <a href="{{url}}">{{url}}</a>. You may now log in with your new account.
|
Thank you for creating an account at <a href="{{url}}/">{{url}}</a>. You may now log in with your new account.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
|
|
|
@ -2,7 +2,7 @@ Welcome
|
||||||
<!---------------->
|
<!---------------->
|
||||||
<html>
|
<html>
|
||||||
<p>
|
<p>
|
||||||
Thank you for creating an account at <a href="{{url}}">{{url}}</a>. Before you can login with your new account, you must verify this email address by clicking the link below.
|
Thank you for creating an account at <a href="{{url}}/">{{url}}</a>. Before you can login with your new account, you must verify this email address by clicking the link below.
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<a href="{{url}}/#/verify-email/?userId={{user_id}}&token={{token}}">
|
<a href="{{url}}/#/verify-email/?userId={{user_id}}&token={{token}}">
|
||||||
|
|
|
@ -96,7 +96,7 @@ Welcome
|
||||||
<table width="100%" cellpadding="0" cellspacing="0" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<table width="100%" cellpadding="0" cellspacing="0" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none; text-align: center;" valign="top" align="center">
|
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none; text-align: center;" valign="top" align="center">
|
||||||
Thank you for creating an account at <a href="{{url}}">{{url}}</a>. Before you can login with your new account, you must verify this email address by clicking the link below.
|
Thank you for creating an account at <a href="{{url}}/">{{url}}</a>. Before you can login with your new account, you must verify this email address by clicking the link below.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
|
|
13
src/util.rs
13
src/util.rs
|
@ -109,7 +109,7 @@ impl<'r, R: Responder<'r>> Responder<'r> for Cached<R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log all the routes from the main base paths list, and the attachments endoint
|
// Log all the routes from the main paths list, and the attachments endpoint
|
||||||
// Effectively ignores, any static file route, and the alive endpoint
|
// Effectively ignores, any static file route, and the alive endpoint
|
||||||
const LOGGED_ROUTES: [&str; 6] = [
|
const LOGGED_ROUTES: [&str; 6] = [
|
||||||
"/api",
|
"/api",
|
||||||
|
@ -157,7 +157,10 @@ impl Fairing for BetterLogging {
|
||||||
}
|
}
|
||||||
let uri = request.uri();
|
let uri = request.uri();
|
||||||
let uri_path = uri.path();
|
let uri_path = uri.path();
|
||||||
if self.0 || LOGGED_ROUTES.iter().any(|r| uri_path.starts_with(r)) {
|
// FIXME: trim_start_matches() could result in over-trimming in pathological cases;
|
||||||
|
// strip_prefix() would be a better option once it's stable.
|
||||||
|
let uri_subpath = uri_path.trim_start_matches(&CONFIG.domain_path());
|
||||||
|
if self.0 || LOGGED_ROUTES.iter().any(|r| uri_subpath.starts_with(r)) {
|
||||||
match uri.query() {
|
match uri.query() {
|
||||||
Some(q) => info!(target: "request", "{} {}?{}", method, uri_path, &q[..q.len().min(30)]),
|
Some(q) => info!(target: "request", "{} {}?{}", method, uri_path, &q[..q.len().min(30)]),
|
||||||
None => info!(target: "request", "{} {}", method, uri_path),
|
None => info!(target: "request", "{} {}", method, uri_path),
|
||||||
|
@ -169,8 +172,10 @@ impl Fairing for BetterLogging {
|
||||||
if !self.0 && request.method() == Method::Options {
|
if !self.0 && request.method() == Method::Options {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let uri_path = request.uri().path();
|
// FIXME: trim_start_matches() could result in over-trimming in pathological cases;
|
||||||
if self.0 || LOGGED_ROUTES.iter().any(|r| uri_path.starts_with(r)) {
|
// strip_prefix() would be a better option once it's stable.
|
||||||
|
let uri_subpath = request.uri().path().trim_start_matches(&CONFIG.domain_path());
|
||||||
|
if self.0 || LOGGED_ROUTES.iter().any(|r| uri_subpath.starts_with(r)) {
|
||||||
let status = response.status();
|
let status = response.status();
|
||||||
if let Some(ref route) = request.route() {
|
if let Some(ref route) = request.route() {
|
||||||
info!(target: "response", "{} => {} {}", route, status.code, status.reason)
|
info!(target: "response", "{} => {} {}", route, status.code, status.reason)
|
||||||
|
|
Laden …
In neuem Issue referenzieren