From 8ae799a7716549d25a21004daab099fe05e03be7 Mon Sep 17 00:00:00 2001 From: Nils Mittler Date: Mon, 20 Feb 2023 16:39:56 +0100 Subject: [PATCH 1/8] Add function to fetch user by email address --- src/api/admin.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/api/admin.rs b/src/api/admin.rs index f22d3bc2..f791d779 100644 --- a/src/api/admin.rs +++ b/src/api/admin.rs @@ -33,6 +33,7 @@ pub fn routes() -> Vec { routes![ get_users_json, get_user_json, + get_user_by_mail_json, post_admin_login, admin_page, invite_user, @@ -331,6 +332,18 @@ async fn users_overview(_token: AdminToken, mut conn: DbConn) -> ApiResult")] +async fn get_user_by_mail_json(mail: String, _token: AdminToken, mut conn: DbConn) -> JsonResult { + if let Some(u) = User::find_by_mail(&mail, &mut conn).await { + let mut usr = u.to_json(&mut conn).await; + usr["UserEnabled"] = json!(u.enabled); + usr["CreatedAt"] = json!(format_naive_datetime_local(&u.created_at, DT_FMT)); + Ok(Json(usr)) + } else { + err_code!("User doesn't exist", Status::NotFound.code); + } +} + #[get("/users/")] async fn get_user_json(uuid: String, _token: AdminToken, mut conn: DbConn) -> JsonResult { let u = get_user_or_404(&uuid, &mut conn).await?; From 569b464157608af0375c25bd392efa5ad1c4cdcb Mon Sep 17 00:00:00 2001 From: r3drun3 Date: Wed, 15 Feb 2023 10:15:42 +0100 Subject: [PATCH 2/8] docs: add build status badge in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1201ab2b..2bec51ee 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ 📢 Note: This project was known as Bitwarden_RS and has been renamed to separate itself from the official Bitwarden server in the hopes of avoiding confusion and trademark/branding issues. Please see [#1642](https://github.com/dani-garcia/vaultwarden/discussions/1642) for more explanation. --- - +[![Build](https://github.com/dani-garcia/vaultwarden/actions/workflows/build.yml/badge.svg)](https://github.com/dani-garcia/vaultwarden/actions/workflows/build.yml) [![Docker Pulls](https://img.shields.io/docker/pulls/vaultwarden/server.svg)](https://hub.docker.com/r/vaultwarden/server) [![Dependency Status](https://deps.rs/repo/github/dani-garcia/vaultwarden/status.svg)](https://deps.rs/repo/github/dani-garcia/vaultwarden) [![GitHub Release](https://img.shields.io/github/release/dani-garcia/vaultwarden.svg)](https://github.com/dani-garcia/vaultwarden/releases/latest) From fc543154c0e53370fe51650c9528c0a32913b872 Mon Sep 17 00:00:00 2001 From: BlackDex Date: Thu, 16 Feb 2023 16:29:24 +0100 Subject: [PATCH 3/8] Validate all needed fields for client API login During the client API login we need to have a `device_identifier`, `device_name` and `device_type`. When these were not provided Vaultwarden would panic. This PR add checks for these fields and makes sure it returns a better error message instead of causing a panic. --- src/api/identity.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/api/identity.rs b/src/api/identity.rs index 039e61d5..bb575cca 100644 --- a/src/api/identity.rs +++ b/src/api/identity.rs @@ -52,6 +52,10 @@ async fn login(data: Form, client_header: ClientHeaders, mut conn: _check_is_some(&data.client_secret, "client_secret cannot be blank")?; _check_is_some(&data.scope, "scope cannot be blank")?; + _check_is_some(&data.device_identifier, "device_identifier cannot be blank")?; + _check_is_some(&data.device_name, "device_name cannot be blank")?; + _check_is_some(&data.device_type, "device_type cannot be blank")?; + _api_key_login(data, &mut user_uuid, &mut conn, &ip).await } t => err!("Invalid type", t), From 59ef82b74001ccdf9447da392f8cb04c5fb7af47 Mon Sep 17 00:00:00 2001 From: BlackDex Date: Thu, 16 Feb 2023 17:29:12 +0100 Subject: [PATCH 4/8] Fix Organization delete when groups are configured With existing groups configured within an org, deleting that org would fail because of Foreign Key issues. This PR fixes this by making sure the groups get deleted before the org does. Fixes #3247 --- src/db/models/group.rs | 7 +++++++ src/db/models/organization.rs | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/db/models/group.rs b/src/db/models/group.rs index 1d2e6062..6f267c10 100644 --- a/src/db/models/group.rs +++ b/src/db/models/group.rs @@ -151,6 +151,13 @@ impl Group { } } + pub async fn delete_all_by_organization(org_uuid: &str, conn: &mut DbConn) -> EmptyResult { + for group in Self::find_by_organization(org_uuid, conn).await { + group.delete(conn).await?; + } + Ok(()) + } + pub async fn find_by_organization(organizations_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { groups::table diff --git a/src/db/models/organization.rs b/src/db/models/organization.rs index a6e4be21..34325b78 100644 --- a/src/db/models/organization.rs +++ b/src/db/models/organization.rs @@ -2,7 +2,7 @@ use num_traits::FromPrimitive; use serde_json::Value; use std::cmp::Ordering; -use super::{CollectionUser, GroupUser, OrgPolicy, OrgPolicyType, TwoFactor, User}; +use super::{CollectionUser, Group, GroupUser, OrgPolicy, OrgPolicyType, TwoFactor, User}; use crate::CONFIG; db_object! { @@ -267,6 +267,7 @@ impl Organization { Collection::delete_all_by_organization(&self.uuid, conn).await?; UserOrganization::delete_all_by_organization(&self.uuid, conn).await?; OrgPolicy::delete_all_by_organization(&self.uuid, conn).await?; + Group::delete_all_by_organization(&self.uuid, conn).await?; db_run! { conn: { diesel::delete(organizations::table.filter(organizations::uuid.eq(self.uuid))) From 20d8886bfadf8c014d3a82be410e7a3a92088bbf Mon Sep 17 00:00:00 2001 From: Misterbabou <58564168+Misterbabou@users.noreply.github.com> Date: Fri, 17 Feb 2023 13:34:57 +0100 Subject: [PATCH 5/8] Fix Collection Read Only access for groups I messed up with identation sorry it's my first PR Fix Collection Read Only access for groups Fix Collection Read Only access for groups With indentation modification --- src/db/models/collection.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/db/models/collection.rs b/src/db/models/collection.rs index 0b40196d..1ae29493 100644 --- a/src/db/models/collection.rs +++ b/src/db/models/collection.rs @@ -64,6 +64,8 @@ impl Collection { Some(_) => { if let Some(uc) = cipher_sync_data.user_collections.get(&self.uuid) { (uc.read_only, uc.hide_passwords) + } else if let Some(cg) = cipher_sync_data.user_collections_groups.get(&self.uuid) { + (cg.read_only, cg.hide_passwords) } else { (false, false) } From 2bb41367bca49329c2ece74096e50fc9a422039d Mon Sep 17 00:00:00 2001 From: Nils Mittler Date: Mon, 20 Feb 2023 16:10:30 +0100 Subject: [PATCH 6/8] Make the admin cookie lifetime adjustable --- .env.template | 3 +++ src/api/admin.rs | 2 +- src/config.rs | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.env.template b/.env.template index d2eb768e..0f8f3c31 100644 --- a/.env.template +++ b/.env.template @@ -335,6 +335,9 @@ ## Allow a burst of requests of up to this size, while maintaining the average indicated by `ADMIN_RATELIMIT_SECONDS`. # ADMIN_RATELIMIT_MAX_BURST=3 +## Set the lifetime of the cookie that is used to authorize admin requests to this value (in minutes). +# ADMIN_COOKIE_LIFETIME=20 + ## Yubico (Yubikey) Settings ## Set your Client ID and Secret Key for Yubikey OTP ## You can generate it here: https://upgrade.yubico.com/getapikey/ diff --git a/src/api/admin.rs b/src/api/admin.rs index f791d779..92c8e3b8 100644 --- a/src/api/admin.rs +++ b/src/api/admin.rs @@ -184,7 +184,7 @@ fn post_admin_login(data: Form, cookies: &CookieJar<'_>, ip: ClientIp let cookie = Cookie::build(COOKIE_NAME, jwt) .path(admin_path()) - .max_age(rocket::time::Duration::minutes(20)) + .max_age(rocket::time::Duration::minutes(CONFIG.admin_cookie_lifetime())) .same_site(SameSite::Strict) .http_only(true) .finish(); diff --git a/src/config.rs b/src/config.rs index fa53c55b..eed2a9cf 100644 --- a/src/config.rs +++ b/src/config.rs @@ -581,6 +581,9 @@ make_config! { /// Max burst size for admin login requests |> Allow a burst of requests of up to this size, while maintaining the average indicated by `admin_ratelimit_seconds` admin_ratelimit_max_burst: u32, false, def, 3; + /// Admin cookie lifetime |> Set the lifetime of the cookie that is used to authorize admin requests to this value (in minutes). + admin_cookie_lifetime: i64, true, def, 20; + /// Enable groups (BETA!) (Know the risks!) |> Enables groups support for organizations (Currently contains known issues!). org_groups_enabled: bool, false, def, false; }, From 6fa1dc50bedb4a391a786fdd30fb3814a9d11f2d Mon Sep 17 00:00:00 2001 From: Nils Mittler Date: Mon, 20 Feb 2023 16:51:09 +0100 Subject: [PATCH 7/8] Apply Admin Session Lifetime to JWT --- src/auth.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auth.rs b/src/auth.rs index 03f14cb8..6f265e9f 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -241,7 +241,7 @@ pub fn generate_admin_claims() -> BasicJwtClaims { let time_now = Utc::now().naive_utc(); BasicJwtClaims { nbf: time_now.timestamp(), - exp: (time_now + Duration::minutes(20)).timestamp(), + exp: (time_now + Duration::minutes(CONFIG.admin_cookie_lifetime())).timestamp(), iss: JWT_ADMIN_ISSUER.to_string(), sub: "admin_panel".to_string(), } From 0f656b4889d67cfd3c05f92f2963a600945128fe Mon Sep 17 00:00:00 2001 From: Nils Mittler Date: Mon, 20 Feb 2023 17:02:14 +0100 Subject: [PATCH 8/8] Apply rewording --- .env.template | 4 ++-- src/api/admin.rs | 2 +- src/auth.rs | 2 +- src/config.rs | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.env.template b/.env.template index 0f8f3c31..3c7e7d1e 100644 --- a/.env.template +++ b/.env.template @@ -335,8 +335,8 @@ ## Allow a burst of requests of up to this size, while maintaining the average indicated by `ADMIN_RATELIMIT_SECONDS`. # ADMIN_RATELIMIT_MAX_BURST=3 -## Set the lifetime of the cookie that is used to authorize admin requests to this value (in minutes). -# ADMIN_COOKIE_LIFETIME=20 +## Set the lifetime of admin sessions to this value (in minutes). +# ADMIN_SESSION_LIFETIME=20 ## Yubico (Yubikey) Settings ## Set your Client ID and Secret Key for Yubikey OTP diff --git a/src/api/admin.rs b/src/api/admin.rs index 92c8e3b8..d875d8be 100644 --- a/src/api/admin.rs +++ b/src/api/admin.rs @@ -184,7 +184,7 @@ fn post_admin_login(data: Form, cookies: &CookieJar<'_>, ip: ClientIp let cookie = Cookie::build(COOKIE_NAME, jwt) .path(admin_path()) - .max_age(rocket::time::Duration::minutes(CONFIG.admin_cookie_lifetime())) + .max_age(rocket::time::Duration::minutes(CONFIG.admin_session_lifetime())) .same_site(SameSite::Strict) .http_only(true) .finish(); diff --git a/src/auth.rs b/src/auth.rs index 6f265e9f..380c6a73 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -241,7 +241,7 @@ pub fn generate_admin_claims() -> BasicJwtClaims { let time_now = Utc::now().naive_utc(); BasicJwtClaims { nbf: time_now.timestamp(), - exp: (time_now + Duration::minutes(CONFIG.admin_cookie_lifetime())).timestamp(), + exp: (time_now + Duration::minutes(CONFIG.admin_session_lifetime())).timestamp(), iss: JWT_ADMIN_ISSUER.to_string(), sub: "admin_panel".to_string(), } diff --git a/src/config.rs b/src/config.rs index eed2a9cf..f3736a1f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -581,8 +581,8 @@ make_config! { /// Max burst size for admin login requests |> Allow a burst of requests of up to this size, while maintaining the average indicated by `admin_ratelimit_seconds` admin_ratelimit_max_burst: u32, false, def, 3; - /// Admin cookie lifetime |> Set the lifetime of the cookie that is used to authorize admin requests to this value (in minutes). - admin_cookie_lifetime: i64, true, def, 20; + /// Admin session lifetime |> Set the lifetime of admin sessions to this value (in minutes). + admin_session_lifetime: i64, true, def, 20; /// Enable groups (BETA!) (Know the risks!) |> Enables groups support for organizations (Currently contains known issues!). org_groups_enabled: bool, false, def, false;