Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow customizing the featureStates #4168

Merged
merged 15 commits into from
Jan 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -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.
# 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
## See https://rocket.rs/v0.4/guide/configuration/ for more details.
# ROCKET_ADDRESS=0.0.0.0
Expand Down
14 changes: 4 additions & 10 deletions src/api/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,14 @@ pub fn events_routes() -> Vec<Route> {
//
// Move this somewhere else
//
use rocket::{serde::json::Json, Catcher, Route};
use serde_json::Value;
use rocket::{serde::json::Json, serde::json::Value, Catcher, Route};

use crate::{
api::{JsonResult, JsonUpcase, Notify, UpdateType},
auth::Headers,
db::DbConn,
error::Error,
util::get_reqwest_client,
util::{get_reqwest_client, parse_experimental_client_feature_flags},
};

#[derive(Serialize, Deserialize, Debug)]
Expand Down Expand Up @@ -193,6 +192,7 @@ fn version() -> Json<&'static str> {
#[get("/config")]
fn config() -> Json<Value> {
let domain = crate::CONFIG.domain();
let feature_states = parse_experimental_client_feature_flags(&crate::CONFIG.experimental_client_feature_flags());
Json(json!({
// Note: The clients use this version to handle backwards compatibility concerns
// This means they expect a version that closely matches the Bitwarden server version
Expand All @@ -213,13 +213,7 @@ fn config() -> Json<Value> {
"notifications": format!("{domain}/notifications"),
"sso": "",
},
"featureStates": {
// 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
},
"featureStates": feature_states,
"object": "config",
}))
}
Expand Down
13 changes: 12 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use reqwest::Url;
use crate::{
db::DbConnType,
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(|| {
Expand Down Expand Up @@ -547,6 +547,9 @@ make_config! {
/// TOTP codes of the previous and next 30 seconds will be invalid.
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.
/// If sending the email fails the login attempt will fail.
require_device_email: bool, true, def, false;
Expand Down Expand Up @@ -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
&& (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())
Expand Down
9 changes: 9 additions & 0 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Web Headers and caching
//
use std::{
collections::HashMap,
io::{Cursor, ErrorKind},
ops::Deref,
};
Expand Down Expand Up @@ -747,3 +748,11 @@ pub fn convert_json_key_lcase_first(src_json: 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
}