mirror of
https://github.com/ViViDboarder/bitwarden_rs.git
synced 2024-11-22 21:26:38 +00:00
Merge remote-tracking branch 'upstream/master' into email-codes
This commit is contained in:
commit
5d50b1ee3c
378
Cargo.lock
generated
378
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -40,8 +40,8 @@ rmpv = "0.4.0"
|
|||||||
chashmap = "2.2.2"
|
chashmap = "2.2.2"
|
||||||
|
|
||||||
# A generic serialization/deserialization framework
|
# A generic serialization/deserialization framework
|
||||||
serde = "1.0.98"
|
serde = "1.0.99"
|
||||||
serde_derive = "1.0.98"
|
serde_derive = "1.0.99"
|
||||||
serde_json = "1.0.40"
|
serde_json = "1.0.40"
|
||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
@ -103,10 +103,10 @@ handlebars = "2.0.1"
|
|||||||
|
|
||||||
# For favicon extraction from main website
|
# For favicon extraction from main website
|
||||||
soup = "0.4.1"
|
soup = "0.4.1"
|
||||||
regex = "1.2.0"
|
regex = "1.2.1"
|
||||||
|
|
||||||
# URL encoding library
|
# URL encoding library
|
||||||
percent-encoding = "2.0.0"
|
percent-encoding = "2.1.0"
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
# Add support for Timestamp type
|
# Add support for Timestamp type
|
||||||
|
@ -1 +1 @@
|
|||||||
nightly-2019-07-09
|
nightly-2019-08-18
|
||||||
|
@ -28,6 +28,7 @@ pub fn routes() -> Vec<Route> {
|
|||||||
invite_user,
|
invite_user,
|
||||||
delete_user,
|
delete_user,
|
||||||
deauth_user,
|
deauth_user,
|
||||||
|
remove_2fa,
|
||||||
update_revision_users,
|
update_revision_users,
|
||||||
post_config,
|
post_config,
|
||||||
delete_config,
|
delete_config,
|
||||||
@ -196,6 +197,18 @@ fn deauth_user(uuid: String, _token: AdminToken, conn: DbConn) -> EmptyResult {
|
|||||||
user.save(&conn)
|
user.save(&conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[post("/users/<uuid>/remove-2fa")]
|
||||||
|
fn remove_2fa(uuid: String, _token: AdminToken, conn: DbConn) -> EmptyResult {
|
||||||
|
let mut user = match User::find_by_uuid(&uuid, &conn) {
|
||||||
|
Some(user) => user,
|
||||||
|
None => err!("User doesn't exist"),
|
||||||
|
};
|
||||||
|
|
||||||
|
TwoFactor::delete_all_by_user(&user.uuid, &conn)?;
|
||||||
|
user.totp_recover = None;
|
||||||
|
user.save(&conn)
|
||||||
|
}
|
||||||
|
|
||||||
#[post("/users/update_revision")]
|
#[post("/users/update_revision")]
|
||||||
fn update_revision_users(_token: AdminToken, conn: DbConn) -> EmptyResult {
|
fn update_revision_users(_token: AdminToken, conn: DbConn) -> EmptyResult {
|
||||||
User::update_all_revisions(&conn)
|
User::update_all_revisions(&conn)
|
||||||
|
@ -132,18 +132,33 @@ fn put_eq_domains(data: JsonUpcase<EquivDomainData>, headers: Headers, conn: DbC
|
|||||||
|
|
||||||
#[get("/hibp/breach?<username>")]
|
#[get("/hibp/breach?<username>")]
|
||||||
fn hibp_breach(username: String) -> JsonResult {
|
fn hibp_breach(username: String) -> JsonResult {
|
||||||
let url = format!("https://haveibeenpwned.com/api/v2/breachedaccount/{}", username);
|
|
||||||
let user_agent = "Bitwarden_RS";
|
let user_agent = "Bitwarden_RS";
|
||||||
|
let url = format!(
|
||||||
|
"https://haveibeenpwned.com/api/v3/breachedaccount/{}?truncateResponse=false&includeUnverified=false",
|
||||||
|
username
|
||||||
|
);
|
||||||
|
|
||||||
use reqwest::{header::USER_AGENT, Client};
|
use reqwest::{header::USER_AGENT, Client};
|
||||||
|
|
||||||
let res = Client::new().get(&url).header(USER_AGENT, user_agent).send()?;
|
if let Some(api_key) = crate::CONFIG.hibp_api_key() {
|
||||||
|
let res = Client::new()
|
||||||
|
.get(&url)
|
||||||
|
.header(USER_AGENT, user_agent)
|
||||||
|
.header("hibp-api-key", api_key)
|
||||||
|
.send()?;
|
||||||
|
|
||||||
// If we get a 404, return a 404, it means no breached accounts
|
// If we get a 404, return a 404, it means no breached accounts
|
||||||
if res.status() == 404 {
|
if res.status() == 404 {
|
||||||
return Err(Error::empty().with_code(404));
|
return Err(Error::empty().with_code(404));
|
||||||
|
}
|
||||||
|
|
||||||
|
let value: Value = res.error_for_status()?.json()?;
|
||||||
|
Ok(Json(value))
|
||||||
|
} else {
|
||||||
|
Ok(Json(json!([{
|
||||||
|
"title": "--- Error! ---",
|
||||||
|
"description": "HaveIBeenPwned API key not set! Go to https://haveibeenpwned.com/API/Key",
|
||||||
|
"logopath": "/bwrs_images/error-x.svg"
|
||||||
|
}])))
|
||||||
}
|
}
|
||||||
|
|
||||||
let value: Value = res.error_for_status()?.json()?;
|
|
||||||
Ok(Json(value))
|
|
||||||
}
|
}
|
||||||
|
@ -94,9 +94,7 @@ fn recover(data: JsonUpcase<RecoverTwoFactor>, conn: DbConn) -> JsonResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove all twofactors from the user
|
// Remove all twofactors from the user
|
||||||
for twofactor in TwoFactor::find_by_user(&user.uuid, &conn) {
|
TwoFactor::delete_all_by_user(&user.uuid, &conn)?;
|
||||||
twofactor.delete(&conn)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the recovery code, not needed without twofactors
|
// Remove the recovery code, not needed without twofactors
|
||||||
user.totp_recover = None;
|
user.totp_recover = None;
|
||||||
|
@ -27,6 +27,7 @@ const ALLOWED_CHARS: &str = "_-.";
|
|||||||
lazy_static! {
|
lazy_static! {
|
||||||
// Reuse the client between requests
|
// Reuse the client between requests
|
||||||
static ref CLIENT: Client = Client::builder()
|
static ref CLIENT: Client = Client::builder()
|
||||||
|
.use_sys_proxy()
|
||||||
.gzip(true)
|
.gzip(true)
|
||||||
.timeout(Duration::from_secs(CONFIG.icon_download_timeout()))
|
.timeout(Duration::from_secs(CONFIG.icon_download_timeout()))
|
||||||
.default_headers(_header_map())
|
.default_headers(_header_map())
|
||||||
|
@ -101,7 +101,13 @@ fn _password_login(data: ConnectData, conn: DbConn, ip: ClientIp) -> JsonResult
|
|||||||
let twofactor_token = twofactor_auth(&user.uuid, &data, &mut device, &conn)?;
|
let twofactor_token = twofactor_auth(&user.uuid, &data, &mut device, &conn)?;
|
||||||
|
|
||||||
if CONFIG.mail_enabled() && new_device {
|
if CONFIG.mail_enabled() && new_device {
|
||||||
mail::send_new_device_logged_in(&user.email, &ip.ip.to_string(), &device.updated_at, &device.name)?
|
if let Err(e) = mail::send_new_device_logged_in(&user.email, &ip.ip.to_string(), &device.updated_at, &device.name) {
|
||||||
|
error!("Error sending new device email: {:#?}", e);
|
||||||
|
|
||||||
|
if CONFIG.require_device_email() {
|
||||||
|
err!("Could not send login notification email. Please contact your administrator.")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common
|
// Common
|
||||||
|
@ -65,11 +65,11 @@ fn alive() -> Json<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[get("/bwrs_images/<filename>")]
|
#[get("/bwrs_images/<filename>")]
|
||||||
fn images(filename: String) -> Result<Content<Vec<u8>>, Error> {
|
fn images(filename: String) -> Result<Content<&'static [u8]>, Error> {
|
||||||
let image_type = ContentType::new("image", "png");
|
|
||||||
match filename.as_ref() {
|
match filename.as_ref() {
|
||||||
"mail-github.png" => Ok(Content(image_type , include_bytes!("../static/images/mail-github.png").to_vec())),
|
"mail-github.png" => Ok(Content(ContentType::PNG, include_bytes!("../static/images/mail-github.png"))),
|
||||||
"logo-gray.png" => Ok(Content(image_type, include_bytes!("../static/images/logo-gray.png").to_vec())),
|
"logo-gray.png" => Ok(Content(ContentType::PNG, include_bytes!("../static/images/logo-gray.png"))),
|
||||||
_ => err!("Image not found")
|
"error-x.svg" => Ok(Content(ContentType::SVG, include_bytes!("../static/images/error-x.svg"))),
|
||||||
|
_ => err!("Image not found"),
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -234,6 +234,9 @@ make_config! {
|
|||||||
/// Enable web vault
|
/// Enable web vault
|
||||||
web_vault_enabled: bool, false, def, true;
|
web_vault_enabled: bool, false, def, true;
|
||||||
|
|
||||||
|
/// HIBP Api Key |> HaveIBeenPwned API Key, request it here: https://haveibeenpwned.com/API/Key
|
||||||
|
hibp_api_key: Pass, true, option;
|
||||||
|
|
||||||
/// Disable icon downloads |> Set to true to disable icon downloading, this would still serve icons from
|
/// Disable icon downloads |> Set to true to disable icon downloading, this would still serve icons from
|
||||||
/// $ICON_CACHE_FOLDER, but it won't produce any external network request. Needs to set $ICON_CACHE_TTL to 0,
|
/// $ICON_CACHE_FOLDER, but it won't produce any external network request. Needs to set $ICON_CACHE_TTL to 0,
|
||||||
/// otherwise it will delete them and they won't be downloaded again.
|
/// otherwise it will delete them and they won't be downloaded again.
|
||||||
@ -269,6 +272,10 @@ make_config! {
|
|||||||
/// Note that the checkbox would still be present, but ignored.
|
/// Note that the checkbox would still be present, but ignored.
|
||||||
disable_2fa_remember: bool, true, def, false;
|
disable_2fa_remember: bool, true, def, false;
|
||||||
|
|
||||||
|
/// Require new device emails |> When a user logs in an email is required to be sent.
|
||||||
|
/// If sending the email fails the login attempt will fail.
|
||||||
|
require_device_email: bool, true, def, false;
|
||||||
|
|
||||||
/// Reload templates (Dev) |> When this is set to true, the templates get reloaded with every request.
|
/// Reload templates (Dev) |> When this is set to true, the templates get reloaded with every request.
|
||||||
/// ONLY use this during development, as it can slow down the server
|
/// ONLY use this during development, as it can slow down the server
|
||||||
reload_templates: bool, true, def, false;
|
reload_templates: bool, true, def, false;
|
||||||
|
6
src/static/images/error-x.svg
Normal file
6
src/static/images/error-x.svg
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="450" height="450" version="1">
|
||||||
|
<circle cx="225" cy="225" r="225" fill="#C33"/>
|
||||||
|
<g fill="#FFF" stroke="#FFF" stroke-width="70">
|
||||||
|
<path d="M107 110l236 237M107 347l236-237"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 241 B |
@ -26,9 +26,13 @@
|
|||||||
{{/each}}
|
{{/each}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div style="flex: 0 0 240px;">
|
<div style="flex: 0 0 300px; font-size: 90%; text-align: right; padding-right: 15px">
|
||||||
<a class="mr-3" href="#" onclick='deauthUser({{jsesc Id}})'>Deauthorize sessions</a>
|
{{#if TwoFactorEnabled}}
|
||||||
<a class="mr-3" href="#" onclick='deleteUser({{jsesc Id}}, {{jsesc Email}})'>Delete User</a>
|
<a class="mr-2" href="#" onclick='remove2fa({{jsesc Id}})'>Remove all 2FA</a>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<a class="mr-2" href="#" onclick='deauthUser({{jsesc Id}})'>Deauthorize sessions</a>
|
||||||
|
<a class="mr-2" href="#" onclick='deleteUser({{jsesc Id}}, {{jsesc Email}})'>Delete User</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -227,6 +231,12 @@
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
function remove2fa(id) {
|
||||||
|
_post("/admin/users/" + id + "/remove-2fa",
|
||||||
|
"2FA removed correctly",
|
||||||
|
"Error removing 2FA");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
function deauthUser(id) {
|
function deauthUser(id) {
|
||||||
_post("/admin/users/" + id + "/deauth",
|
_post("/admin/users/" + id + "/deauth",
|
||||||
"Sessions deauthorized correctly",
|
"Sessions deauthorized correctly",
|
||||||
|
Loading…
Reference in New Issue
Block a user