Allow customizing the featureStates (#4168)

* Allow customizing the featureStates

Use a comma separated list of features to enable using the FEATURE_FLAGS env variable

* Move feature flag parsing to util

* Fix formatting

* Update supported feature flags

* Rename feature_flags to experimental_client_feature_flags

Additionally, use a caret (^) instead of an exclamation mark (!) to disable features

* Fix formatting issue.

* Add documentation to env template

* Remove functionality to disable feature flags

* Fix JSON key for feature states

* Convert error to warning when feature flag is unrecognized

* Simplify parsing of feature flags

* Fix default value of feature flags in env template

* Fix formatting
This commit is contained in:
Philipp Kolberg 2024-01-01 08:44:02 -06:00 committed by GitHub
parent 76a3f0f531
commit 98b2178c7d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 36 additions and 11 deletions

View file

@ -390,6 +390,17 @@
## In any case, if a code has been used it can not be used again, also codes which predates it will be invalid. ## In any case, if a code has been used it can not be used again, also codes which predates it will be invalid.
# AUTHENTICATOR_DISABLE_TIME_DRIFT=false # AUTHENTICATOR_DISABLE_TIME_DRIFT=false
## Client Settings
## Enable experimental feature flags for clients.
## This is a comma-separated list of flags, e.g. "flag1,flag2,flag3".
##
## The following flags are available:
## - "autofill-overlay": Add an overlay menu to form fields for quick access to credentials.
## - "autofill-v2": Use the new autofill implementation.
## - "browser-fileless-import": Directly import credentials from other providers without a file.
## - "fido2-vault-credentials": Enable the use of FIDO2 security keys as second factor.
## EXPERIMENTAL_CLIENT_FEATURE_FLAGS=fido2-vault-credentials
## Rocket specific settings ## Rocket specific settings
## See https://rocket.rs/v0.4/guide/configuration/ for more details. ## See https://rocket.rs/v0.4/guide/configuration/ for more details.
# ROCKET_ADDRESS=0.0.0.0 # ROCKET_ADDRESS=0.0.0.0

View file

@ -46,15 +46,14 @@ pub fn events_routes() -> Vec<Route> {
// //
// Move this somewhere else // Move this somewhere else
// //
use rocket::{serde::json::Json, Catcher, Route}; use rocket::{serde::json::Json, serde::json::Value, Catcher, Route};
use serde_json::Value;
use crate::{ use crate::{
api::{JsonResult, JsonUpcase, Notify, UpdateType}, api::{JsonResult, JsonUpcase, Notify, UpdateType},
auth::Headers, auth::Headers,
db::DbConn, db::DbConn,
error::Error, error::Error,
util::get_reqwest_client, util::{get_reqwest_client, parse_experimental_client_feature_flags},
}; };
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
@ -192,6 +191,7 @@ fn version() -> Json<&'static str> {
#[get("/config")] #[get("/config")]
fn config() -> Json<Value> { fn config() -> Json<Value> {
let domain = crate::CONFIG.domain(); let domain = crate::CONFIG.domain();
let feature_states = parse_experimental_client_feature_flags(&crate::CONFIG.experimental_client_feature_flags());
Json(json!({ Json(json!({
// Note: The clients use this version to handle backwards compatibility concerns // Note: The clients use this version to handle backwards compatibility concerns
// This means they expect a version that closely matches the Bitwarden server version // This means they expect a version that closely matches the Bitwarden server version
@ -212,13 +212,7 @@ fn config() -> Json<Value> {
"notifications": format!("{domain}/notifications"), "notifications": format!("{domain}/notifications"),
"sso": "", "sso": "",
}, },
"featureStates": { "featureStates": feature_states,
// Any feature flags that we want the clients to use
// Can check the enabled ones at:
// https://vault.bitwarden.com/api/config
"fido2-vault-credentials": true, // Passkey support
"autofill-v2": false, // Disabled because it is causing issues https://github.com/dani-garcia/vaultwarden/discussions/4052
},
"object": "config", "object": "config",
})) }))
} }

View file

@ -9,7 +9,7 @@ use reqwest::Url;
use crate::{ use crate::{
db::DbConnType, db::DbConnType,
error::Error, error::Error,
util::{get_env, get_env_bool}, util::{get_env, get_env_bool, parse_experimental_client_feature_flags},
}; };
static CONFIG_FILE: Lazy<String> = Lazy::new(|| { static CONFIG_FILE: Lazy<String> = Lazy::new(|| {
@ -547,6 +547,9 @@ make_config! {
/// TOTP codes of the previous and next 30 seconds will be invalid. /// TOTP codes of the previous and next 30 seconds will be invalid.
authenticator_disable_time_drift: bool, true, def, false; authenticator_disable_time_drift: bool, true, def, false;
/// Customize the enabled feature flags on the clients |> This is a comma separated list of feature flags to enable.
experimental_client_feature_flags: String, false, def, "fido2-vault-credentials".to_string();
/// Require new device emails |> When a user logs in an email is required to be sent. /// 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. /// If sending the email fails the login attempt will fail.
require_device_email: bool, true, def, false; require_device_email: bool, true, def, false;
@ -751,6 +754,14 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> {
) )
} }
const KNOWN_FLAGS: &[&str] =
&["autofill-overlay", "autofill-v2", "browser-fileless-import", "fido2-vault-credentials"];
for flag in parse_experimental_client_feature_flags(&cfg.experimental_client_feature_flags).keys() {
if !KNOWN_FLAGS.contains(&flag.as_str()) {
warn!("The experimental client feature flag {flag:?} is unrecognized. Please ensure the feature flag is spelled correctly and that it is supported in this version.");
}
}
if cfg._enable_duo if cfg._enable_duo
&& (cfg.duo_host.is_some() || cfg.duo_ikey.is_some() || cfg.duo_skey.is_some()) && (cfg.duo_host.is_some() || cfg.duo_ikey.is_some() || cfg.duo_skey.is_some())
&& !(cfg.duo_host.is_some() && cfg.duo_ikey.is_some() && cfg.duo_skey.is_some()) && !(cfg.duo_host.is_some() && cfg.duo_ikey.is_some() && cfg.duo_skey.is_some())

View file

@ -2,6 +2,7 @@
// Web Headers and caching // Web Headers and caching
// //
use std::{ use std::{
collections::HashMap,
io::{Cursor, ErrorKind}, io::{Cursor, ErrorKind},
ops::Deref, ops::Deref,
}; };
@ -747,3 +748,11 @@ pub fn convert_json_key_lcase_first(src_json: Value) -> Value {
value => value, value => value,
} }
} }
/// Parses the experimental client feature flags string into a HashMap.
pub fn parse_experimental_client_feature_flags(experimental_client_feature_flags: &str) -> HashMap<String, bool> {
let feature_states =
experimental_client_feature_flags.to_lowercase().split(',').map(|f| (f.trim().to_owned(), true)).collect();
feature_states
}