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

Showcase how state can be used in the cli #958

Draft
wants to merge 6 commits into
base: sqlite-wasm
Choose a base branch
from
Draft
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
6 changes: 6 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 40 additions & 0 deletions crates/bitwarden-core/src/auth/auth_repository.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use bitwarden_crypto::Kdf;

use crate::platform::{SettingsRepository, SettingsRepositoryError};

const SETTINGS_KEY: &str = "auth";

#[derive(Debug, serde::Serialize, serde::Deserialize)]

Check warning on line 7 in crates/bitwarden-core/src/auth/auth_repository.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/auth/auth_repository.rs#L7

Added line #L7 was not covered by tests
pub struct AuthSettings {
pub email: String,
pub token: String,
pub refresh_token: Option<String>,
pub kdf: Kdf,

pub user_key: String,
pub private_key: String,
}

pub struct AuthRepository {
settings_repository: SettingsRepository,
}

impl AuthRepository {
pub fn new(settings_repository: SettingsRepository) -> Self {
Self {
settings_repository,
}
}

pub(crate) async fn save(&self, setting: AuthSettings) -> Result<(), SettingsRepositoryError> {
self.settings_repository.set(SETTINGS_KEY, &setting).await?;

Check warning on line 30 in crates/bitwarden-core/src/auth/auth_repository.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/auth/auth_repository.rs#L29-L30

Added lines #L29 - L30 were not covered by tests

Ok(())
}

Check warning on line 33 in crates/bitwarden-core/src/auth/auth_repository.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/auth/auth_repository.rs#L32-L33

Added lines #L32 - L33 were not covered by tests

pub(crate) async fn get(&self) -> Result<Option<AuthSettings>, SettingsRepositoryError> {
let settings = self.settings_repository.get(SETTINGS_KEY).await?;

Check warning on line 36 in crates/bitwarden-core/src/auth/auth_repository.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/auth/auth_repository.rs#L35-L36

Added lines #L35 - L36 were not covered by tests

Ok(settings)
}

Check warning on line 39 in crates/bitwarden-core/src/auth/auth_repository.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/auth/auth_repository.rs#L38-L39

Added lines #L38 - L39 were not covered by tests
}
20 changes: 16 additions & 4 deletions crates/bitwarden-core/src/auth/client_auth.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#[cfg(feature = "internal")]
use bitwarden_crypto::{AsymmetricEncString, DeviceKey, EncString, Kdf, TrustDeviceResponse};

#[cfg(feature = "state")]
use super::{unlock, AuthRepository, UnlockError};
#[cfg(feature = "internal")]
use crate::auth::login::NewAuthRequestResponse;
#[cfg(feature = "secrets")]
Expand All @@ -25,6 +27,8 @@

pub struct ClientAuth<'a> {
pub(crate) client: &'a crate::Client,
#[cfg(feature = "state")]
pub(super) repository: AuthRepository,
}

impl<'a> ClientAuth<'a> {
Expand Down Expand Up @@ -132,10 +136,7 @@
pub fn trust_device(&self) -> Result<TrustDeviceResponse> {
trust_device(self.client)
}
}

#[cfg(feature = "internal")]
impl<'a> ClientAuth<'a> {
pub async fn login_device(
&self,
email: String,
Expand All @@ -153,6 +154,13 @@
}
}

#[cfg(feature = "state")]
impl<'a> ClientAuth<'a> {
pub async fn unlock(&self, password: String) -> Result<(), UnlockError> {
unlock(self.client, password).await
}

Check warning on line 161 in crates/bitwarden-core/src/auth/client_auth.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/auth/client_auth.rs#L159-L161

Added lines #L159 - L161 were not covered by tests
}

#[cfg(feature = "internal")]
fn trust_device(client: &Client) -> Result<TrustDeviceResponse> {
let enc = client.internal.get_encryption_settings()?;
Expand All @@ -164,7 +172,11 @@

impl<'a> Client {
pub fn auth(&'a self) -> ClientAuth<'a> {
ClientAuth { client: self }
ClientAuth {
client: self,
#[cfg(feature = "state")]
repository: AuthRepository::new(self.platform().settings_repository),
}
}
}

Expand Down
33 changes: 28 additions & 5 deletions crates/bitwarden-core/src/auth/login/api_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[cfg(feature = "state")]
use crate::auth::AuthSettings;
use crate::{
auth::{
api::{request::ApiTokenRequest, response::IdentityTokenResponse},
Expand Down Expand Up @@ -45,16 +47,37 @@
.set_login_method(LoginMethod::User(UserLoginMethod::ApiKey {
client_id: input.client_id.to_owned(),
client_secret: input.client_secret.to_owned(),
email,
kdf,
email: email.clone(),
kdf: kdf.clone(),

Check warning on line 51 in crates/bitwarden-core/src/auth/login/api_key.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/auth/login/api_key.rs#L50-L51

Added lines #L50 - L51 were not covered by tests
}));

let user_key: EncString = require!(r.key.as_deref()).parse()?;
let private_key: EncString = require!(r.private_key.as_deref()).parse()?;

client
.internal
.initialize_user_crypto_master_key(master_key, user_key, private_key)?;
client.internal.initialize_user_crypto_master_key(
master_key,
user_key.clone(),
private_key.clone(),
)?;

Check warning on line 61 in crates/bitwarden-core/src/auth/login/api_key.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/auth/login/api_key.rs#L57-L61

Added lines #L57 - L61 were not covered by tests

#[cfg(feature = "state")]
{
let setting = AuthSettings {
email: email.clone(),
token: r.access_token.clone(),
refresh_token: r.refresh_token.clone(),
kdf: kdf.clone(),
user_key: user_key.to_string(),
private_key: private_key.to_string(),
};

client
.auth()
.repository
.save(setting)
.await
.expect("Save settings");

Check warning on line 79 in crates/bitwarden-core/src/auth/login/api_key.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/auth/login/api_key.rs#L65-L79

Added lines #L65 - L79 were not covered by tests
}
}

ApiKeyLoginResponse::process_response(response)
Expand Down
14 changes: 14 additions & 0 deletions crates/bitwarden-core/src/auth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,20 @@ pub use tde::RegisterTdeKeyResponse;
#[cfg(feature = "internal")]
use crate::error::Result;

#[cfg(feature = "state")]
#[path = ""]
mod state {
mod auth_repository;
pub use auth_repository::{AuthRepository, AuthSettings};

mod unlock;
pub(super) use unlock::unlock;
pub use unlock::UnlockError;
}

#[cfg(feature = "state")]
pub use state::*;

#[cfg(feature = "internal")]
fn determine_password_hash(
email: &str,
Expand Down
42 changes: 42 additions & 0 deletions crates/bitwarden-core/src/auth/unlock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use bitwarden_crypto::{CryptoError, MasterKey};
use thiserror::Error;

use crate::{platform::SettingsRepositoryError, Client};

#[derive(Debug, Error)]

Check warning on line 6 in crates/bitwarden-core/src/auth/unlock.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/auth/unlock.rs#L6

Added line #L6 was not covered by tests
pub enum UnlockError {
#[error(transparent)]
SettingRepository(#[from] SettingsRepositoryError),

#[error(transparent)]
Crypto(#[from] CryptoError),

#[error(transparent)]
Error(#[from] crate::Error),

#[error("The client is not authenticated or the session has expired")]
NotAuthenticated,
}

pub(crate) async fn unlock(
client: &Client,
password: String,
) -> std::result::Result<(), UnlockError> {
let settings = client
.auth()
.repository
.get()
.await?
.ok_or(UnlockError::NotAuthenticated)?;

Check warning on line 30 in crates/bitwarden-core/src/auth/unlock.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/auth/unlock.rs#L21-L30

Added lines #L21 - L30 were not covered by tests

// client.internal.set_login_method(UserLoginMethod::ApiKey { client_id: (), client_secret: (),
// email: (), kdf: () })
let master_key = MasterKey::derive(&password, &settings.email, &settings.kdf)?;
client.internal.initialize_user_crypto_master_key(
master_key,
settings.user_key.parse()?,
settings.private_key.parse()?,
)?;

Check warning on line 39 in crates/bitwarden-core/src/auth/unlock.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/auth/unlock.rs#L34-L39

Added lines #L34 - L39 were not covered by tests

Ok(())
}

Check warning on line 42 in crates/bitwarden-core/src/auth/unlock.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/auth/unlock.rs#L41-L42

Added lines #L41 - L42 were not covered by tests
10 changes: 9 additions & 1 deletion crates/bitwarden-core/src/platform/client_platform.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#[cfg(feature = "state")]
use super::settings_repository::SettingsRepository;
use super::{
generate_fingerprint::{generate_fingerprint, generate_user_fingerprint},
get_user_api_key, FingerprintRequest, FingerprintResponse, SecretVerificationRequest,
Expand All @@ -7,6 +9,8 @@ use crate::{error::Result, Client};

pub struct ClientPlatform<'a> {
pub(crate) client: &'a Client,
#[cfg(feature = "state")]
pub settings_repository: SettingsRepository,
}

impl<'a> ClientPlatform<'a> {
Expand All @@ -28,6 +32,10 @@ impl<'a> ClientPlatform<'a> {

impl<'a> Client {
pub fn platform(&'a self) -> ClientPlatform<'a> {
ClientPlatform { client: self }
ClientPlatform {
client: self,
#[cfg(feature = "state")]
settings_repository: SettingsRepository::new(self.internal.db.clone()),
}
}
}
5 changes: 4 additions & 1 deletion crates/bitwarden-core/src/platform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ pub mod client_platform;
mod generate_fingerprint;
mod get_user_api_key;
mod secret_verification_request;

#[cfg(feature = "state")]
mod settings_repository;
pub use generate_fingerprint::{FingerprintRequest, FingerprintResponse};
pub(crate) use get_user_api_key::get_user_api_key;
pub use get_user_api_key::UserApiKeyResponse;
pub use secret_verification_request::SecretVerificationRequest;
#[cfg(feature = "state")]
pub use settings_repository::{SettingsRepository, SettingsRepositoryError};
62 changes: 62 additions & 0 deletions crates/bitwarden-core/src/platform/settings_repository.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use std::sync::Arc;

use bitwarden_db::{params, Database, DatabaseError, DatabaseTrait};
use serde::{de::DeserializeOwned, Serialize};
use thiserror::Error;
use tokio::sync::Mutex;

#[derive(Debug, Error)]

Check warning on line 8 in crates/bitwarden-core/src/platform/settings_repository.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/platform/settings_repository.rs#L8

Added line #L8 was not covered by tests
pub enum SettingsRepositoryError {
#[error(transparent)]
Database(#[from] DatabaseError),
#[error(transparent)]
Serde(#[from] serde_json::Error),
}

pub struct SettingsRepository {
db: Arc<Mutex<Database>>,
}

impl SettingsRepository {
pub fn new(db: Arc<Mutex<Database>>) -> Self {
Self { db: db.clone() }
}

pub async fn get<T: DeserializeOwned>(
&self,
key: &str,
) -> Result<Option<T>, SettingsRepositoryError> {
let guard = self.db.lock().await;

Check warning on line 29 in crates/bitwarden-core/src/platform/settings_repository.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/platform/settings_repository.rs#L25-L29

Added lines #L25 - L29 were not covered by tests

let res = guard
.query_map(
"SELECT value FROM settings WHERE key = ?1",
[key],
|row| -> Result<String, _> { row.get(0) },
)
.await?
.first()
.map(|x| serde_json::from_str::<T>(x))
.transpose()?;

Check warning on line 40 in crates/bitwarden-core/src/platform/settings_repository.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/platform/settings_repository.rs#L31-L40

Added lines #L31 - L40 were not covered by tests

Ok(res)
}

Check warning on line 43 in crates/bitwarden-core/src/platform/settings_repository.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/platform/settings_repository.rs#L42-L43

Added lines #L42 - L43 were not covered by tests

pub async fn set<T: Serialize>(
&self,
key: &str,
value: &T,
) -> Result<(), SettingsRepositoryError> {
let value = serde_json::to_string(value)?;
let guard = self.db.lock().await;

Check warning on line 51 in crates/bitwarden-core/src/platform/settings_repository.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/platform/settings_repository.rs#L45-L51

Added lines #L45 - L51 were not covered by tests

guard
.execute(
"INSERT OR REPLACE INTO settings (key, value) VALUES (?1, ?2)",
params![key, value],
)
.await?;

Check warning on line 58 in crates/bitwarden-core/src/platform/settings_repository.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/platform/settings_repository.rs#L53-L58

Added lines #L53 - L58 were not covered by tests

Ok(())
}

Check warning on line 61 in crates/bitwarden-core/src/platform/settings_repository.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/platform/settings_repository.rs#L60-L61

Added lines #L60 - L61 were not covered by tests
}
13 changes: 12 additions & 1 deletion crates/bw/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,26 @@ repository.workspace = true
license-file.workspace = true

[dependencies]
bitwarden = { workspace = true, features = ["internal"] }
bat = { version = "0.24.0", features = [
"regex-onig",
], default-features = false }
bitwarden = { workspace = true, features = ["internal", "state"] }
bitwarden-cli = { workspace = true }
bitwarden-crypto = { workspace = true }
chrono = { version = "0.4.38", features = [
"clock",
"std",
], default-features = false }
clap = { version = "4.5.4", features = ["derive", "env"] }
comfy-table = "7.1.1"
color-eyre = "0.6.3"
env_logger = "0.11.1"
inquire = "0.7.0"
log = "0.4.20"
tokio = { version = "1.36.0", features = ["rt-multi-thread", "macros"] }
serde = "1.0.196"
serde_json = ">=1.0.96, <2"
serde_yaml = "0.9"

[dev-dependencies]
tempfile = "3.10.0"
Expand Down
7 changes: 7 additions & 0 deletions crates/bw/src/auth/login.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@
})
.await?;

let res = client
.vault()
.sync(&SyncRequest {
exclude_subdomains: Some(true),
})
.await?;

Check warning on line 119 in crates/bw/src/auth/login.rs

View check run for this annotation

Codecov / codecov/patch

crates/bw/src/auth/login.rs#L114-L119

Added lines #L114 - L119 were not covered by tests

debug!("{:?}", result);

Ok(())
Expand Down
1 change: 1 addition & 0 deletions crates/bw/src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub(crate) mod vault;
Loading
Loading