From 9a5f3a501563a3a314c71619f893db3eed14333e Mon Sep 17 00:00:00 2001 From: Nils Mittler Date: Mon, 20 Feb 2023 16:10:30 +0100 Subject: [PATCH 1/7] 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 f22d3bc2..66f26bcc 100644 --- a/src/api/admin.rs +++ b/src/api/admin.rs @@ -183,7 +183,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 2eb4f290a57764ecaac75e7f6a7c89a557ee7c94 Mon Sep 17 00:00:00 2001 From: Nils Mittler Date: Mon, 20 Feb 2023 16:51:09 +0100 Subject: [PATCH 2/7] 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 a947e434f00920ca2c5491b75ab11e6fbbb86a1c Mon Sep 17 00:00:00 2001 From: Nils Mittler Date: Mon, 20 Feb 2023 17:02:14 +0100 Subject: [PATCH 3/7] 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 66f26bcc..8bfc1f21 100644 --- a/src/api/admin.rs +++ b/src/api/admin.rs @@ -183,7 +183,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; From a3084feaee53e3ab36026f8c0cbc8635e1b80216 Mon Sep 17 00:00:00 2001 From: r3drun3 Date: Wed, 15 Feb 2023 10:15:42 +0100 Subject: [PATCH 4/7] 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 7026e004e1e32c6a091bf10fd948b3725f562c06 Mon Sep 17 00:00:00 2001 From: BlackDex Date: Thu, 16 Feb 2023 16:29:24 +0100 Subject: [PATCH 5/7] 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 2dcbb2be59340a0cdf2ab964d6edcfc888aa2f09 Mon Sep 17 00:00:00 2001 From: BlackDex Date: Thu, 16 Feb 2023 17:29:12 +0100 Subject: [PATCH 6/7] 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 8fcbc58ee216d4bc3b3870fcf071b668bb431cef 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 7/7] 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) }