Skip to content

Commit

Permalink
Merge branch 'main' into km/pm-12400/private-key-regen
Browse files Browse the repository at this point in the history
  • Loading branch information
Thomas-Avery committed Nov 26, 2024
2 parents 3ac3cf6 + 130ac6f commit cc34372
Show file tree
Hide file tree
Showing 32 changed files with 988 additions and 79 deletions.
219 changes: 187 additions & 32 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ bitwarden-generators = { path = "crates/bitwarden-generators", version = "=1.0.0
bitwarden-send = { path = "crates/bitwarden-send", version = "=1.0.0" }
bitwarden-sm = { path = "bitwarden_license/bitwarden-sm", version = "=1.0.0" }
bitwarden-vault = { path = "crates/bitwarden-vault", version = "=1.0.0" }
bitwarden-error = { path = "crates/bitwarden-error", version = "=1.0.0" }
bitwarden-error-macro = { path = "crates/bitwarden-error-macro", version = "=1.0.0" }

# External crates that are expected to maintain a consistent version across all crates
chrono = { version = ">=0.4.26, <0.5", features = [
Expand Down Expand Up @@ -56,6 +58,7 @@ uniffi = "=0.28.1"
uuid = { version = ">=1.3.3, <2.0", features = ["serde", "v4"] }
validator = { version = "0.18.1", features = ["derive"] }
wasm-bindgen = { version = ">=0.2.91, <0.3", features = ["serde-serialize"] }
js-sys = { version = ">=0.3.72, <0.4" }
wasm-bindgen-futures = "0.4.41"

[workspace.lints.clippy]
Expand Down
7 changes: 6 additions & 1 deletion crates/bitwarden-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ no-memory-hardening = [
] # Disable memory hardening features
uniffi = ["bitwarden-crypto/uniffi", "dep:uniffi"] # Uniffi bindings
secrets = [] # Secrets manager API
wasm = ["dep:wasm-bindgen", "dep:tsify-next"] # WASM support
wasm = [
"bitwarden-error/wasm",
"dep:wasm-bindgen",
"dep:tsify-next",
] # WASM support

[dependencies]
base64 = ">=0.22.1, <0.23"
Expand Down Expand Up @@ -49,6 +53,7 @@ wasm-bindgen = { workspace = true, optional = true }
zeroize = { version = ">=1.7.0, <2.0", features = ["derive", "aarch64"] }
zxcvbn = { version = ">=3.0.1, <4.0", optional = true }
tsify-next = { workspace = true, optional = true }
bitwarden-error = { workspace = true }

[target.'cfg(not(target_arch="wasm32"))'.dependencies]
# By default, we use rustls as the TLS stack and rust-platform-verifier to support user-installed root certificates
Expand Down
2 changes: 2 additions & 0 deletions crates/bitwarden-core/src/client/encryption_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ use std::collections::HashMap;
use bitwarden_crypto::{AsymmetricCryptoKey, CryptoError, KeyContainer, SymmetricCryptoKey};
#[cfg(feature = "internal")]
use bitwarden_crypto::{AsymmetricEncString, EncString, MasterKey};
use bitwarden_error::prelude::*;
use thiserror::Error;
use uuid::Uuid;

#[cfg(feature = "internal")]
use crate::error::Result;
use crate::VaultLocked;

#[bitwarden_error(flat)]
#[derive(Debug, Error)]
pub enum EncryptionSettingsError {
#[error("Cryptography error, {0}")]
Expand Down
2 changes: 2 additions & 0 deletions crates/bitwarden-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::{borrow::Cow, fmt::Debug};

use bitwarden_api_api::apis::Error as ApiError;
use bitwarden_api_identity::apis::Error as IdentityError;
use bitwarden_error::prelude::*;
use log::debug;
use reqwest::StatusCode;
use thiserror::Error;
Expand All @@ -12,6 +13,7 @@ use validator::ValidationErrors;
#[cfg(feature = "internal")]
use crate::client::encryption_settings::EncryptionSettingsError;

#[bitwarden_error(flat, export_as = "CoreError")]
#[derive(Debug, Error)]
pub enum Error {
#[error(transparent)]
Expand Down
33 changes: 33 additions & 0 deletions crates/bitwarden-error-macro/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
[package]
name = "bitwarden-error-macro"
version.workspace = true
authors.workspace = true
edition.workspace = true
rust-version.workspace = true
homepage.workspace = true
repository.workspace = true
license-file.workspace = true
keywords.workspace = true

[features]
wasm = ["bitwarden-error/wasm"]

[dependencies]
darling = "0.20.10"
proc-macro2 = "1.0.89"
quote = "1.0.37"
syn = "2.0.87"

[lints]
workspace = true

[lib]
proc-macro = true

[dev-dependencies]
bitwarden-error.workspace = true
serde.workspace = true
thiserror.workspace = true
tsify-next.workspace = true
js-sys.workspace = true
wasm-bindgen.workspace = true
23 changes: 23 additions & 0 deletions crates/bitwarden-error-macro/src/args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use darling::FromMeta;

#[derive(FromMeta)]
pub(crate) struct BitwardenErrorArgs {
#[darling(flatten)]
pub error_type: BitwardenErrorType,

#[darling(default)]
pub export_as: Option<String>,
}

#[derive(FromMeta)]
#[darling(rename_all = "snake_case")]
pub(crate) enum BitwardenErrorType {
/// The error is going to be converted into a string using the `ToString` trait
Basic,

/// The error is going to be converted into a flat error using the `FlatError` trait
Flat,

/// The entire error stack is going to be made available using `serde`
Full,
}
49 changes: 49 additions & 0 deletions crates/bitwarden-error-macro/src/attribute.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use darling::{ast::NestedMeta, FromMeta};
use quote::format_ident;

use crate::args::{BitwardenErrorArgs, BitwardenErrorType};

pub(crate) fn bitwarden_error(
args: proc_macro::TokenStream,
item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let attr_args = match NestedMeta::parse_meta_list(args.into()) {
Ok(v) => v,
Err(e) => {
return proc_macro::TokenStream::from(darling::Error::from(e).write_errors());
}
};

let args = match BitwardenErrorArgs::from_list(&attr_args) {
Ok(params) => params,
Err(error) => {
return proc_macro::TokenStream::from(error.write_errors());
}
};

let input = syn::parse_macro_input!(item as syn::DeriveInput);
let type_identifier = &input.ident;
let export_as_identifier = &args
.export_as
.as_ref()
.map(|export_as| format_ident!("{}", export_as))
.unwrap_or(input.ident.clone());

match args.error_type {
BitwardenErrorType::Basic => crate::basic::attribute::bitwarden_error_basic(
&input,
type_identifier,
export_as_identifier,
),
BitwardenErrorType::Flat => crate::flat::attribute::bitwarden_error_flat(
&input,
type_identifier,
export_as_identifier,
),
BitwardenErrorType::Full => crate::full::attribute::bitwarden_error_full(
&input,
type_identifier,
export_as_identifier,
),
}
}
61 changes: 61 additions & 0 deletions crates/bitwarden-error-macro/src/basic/attribute.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use quote::quote;

pub(crate) fn bitwarden_error_basic(
input: &syn::DeriveInput,
type_identifier: &proc_macro2::Ident,
export_as_identifier: &proc_macro2::Ident,
) -> proc_macro::TokenStream {
let wasm =
cfg!(feature = "wasm").then(|| basic_error_wasm(type_identifier, export_as_identifier));
quote! {
#input

#wasm
}
.into()
}

fn basic_error_wasm(
type_identifier: &proc_macro2::Ident,
export_as_identifier: &proc_macro2::Ident,
) -> proc_macro2::TokenStream {
let export_as_identifier_str = export_as_identifier.to_string();
let is_error_function_name = format!("is{}", export_as_identifier);
let ts_code_str = format!(
r##"r#"
export interface {export_as_identifier} extends Error {{
name: "{export_as_identifier}";
}};
export function {is_error_function_name}(error: any): error is {export_as_identifier};
"#"##
);
let ts_code: proc_macro2::TokenStream = ts_code_str
.parse()
.expect("Could not generate TypeScript code");

quote! {
const _: () = {
use bitwarden_error::wasm_bindgen::prelude::*;

#[wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = #ts_code;

#[wasm_bindgen(js_name = #is_error_function_name, skip_typescript)]
pub fn is_error(error: &JsValue) -> bool {
let name_js_value = bitwarden_error::js_sys::Reflect::get(&error, &JsValue::from_str("name")).unwrap_or(JsValue::NULL);
let name = name_js_value.as_string().unwrap_or_default();
name == #export_as_identifier_str
}

#[automatically_derived]
impl From<#type_identifier> for JsValue {
fn from(error: #type_identifier) -> Self {
let js_error = SdkJsError::new(error.to_string());
js_error.set_name(#export_as_identifier_str.to_owned());
js_error.into()
}
}
};
}
}
1 change: 1 addition & 0 deletions crates/bitwarden-error-macro/src/basic/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod attribute;
115 changes: 115 additions & 0 deletions crates/bitwarden-error-macro/src/flat/attribute.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use quote::quote;
use syn::Data;

pub(crate) fn bitwarden_error_flat(
input: &syn::DeriveInput,
type_identifier: &proc_macro2::Ident,
export_as_identifier: &proc_macro2::Ident,
) -> proc_macro::TokenStream {
match &input.data {
Data::Enum(data) => {
let variant_names = data.variants.iter().map(|variant| &variant.ident);
let match_arms = data.variants.iter().map(|variant| {
let variant_ident = &variant.ident;
let variant_str = variant_ident.to_string();

match variant.fields {
syn::Fields::Unit => {
quote! {
#type_identifier::#variant_ident => #variant_str
}
}
syn::Fields::Named(_) => {
quote! {
#type_identifier::#variant_ident { .. } => #variant_str
}
}
syn::Fields::Unnamed(_) => {
quote! {
#type_identifier::#variant_ident(..) => #variant_str
}
}
}
});

let wasm = cfg!(feature = "wasm").then(|| {
flat_error_wasm(
type_identifier,
export_as_identifier,
&variant_names.collect::<Vec<_>>(),
)
});

quote! {
#input
#wasm

#[automatically_derived]
impl ::bitwarden_error::prelude::FlatError for #type_identifier {
fn error_variant(&self) -> &'static str {
match &self {
#(#match_arms), *
}
}
}
}
.into()
}
_ => syn::Error::new_spanned(input, "bitwarden_error can only be used with enums")
.to_compile_error()
.into(),
}
}

fn flat_error_wasm(
type_identifier: &proc_macro2::Ident,
export_as_identifier: &proc_macro2::Ident,
variant_names: &[&proc_macro2::Ident],
) -> proc_macro2::TokenStream {
let export_as_identifier_str = export_as_identifier.to_string();
let is_error_function_name = format!("is{}", export_as_identifier);
let ts_variant_names = variant_names
.iter()
.map(|vn| format!(r#""{vn}""#))
.collect::<Vec<String>>()
.join("|");
let ts_code_str = format!(
r##"r#"
export interface {export_as_identifier_str} extends Error {{
name: "{export_as_identifier_str}";
variant: {ts_variant_names};
}};
export function {is_error_function_name}(error: any): error is {export_as_identifier_str};
"#"##,
);
let ts_code: proc_macro2::TokenStream = ts_code_str
.parse()
.expect("Could not generate TypeScript code");

quote! {
const _: () = {
use bitwarden_error::wasm_bindgen::prelude::*;

#[wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = #ts_code;

#[wasm_bindgen(js_name = #is_error_function_name, skip_typescript)]
pub fn is_error(error: &JsValue) -> bool {
let name_js_value = bitwarden_error::js_sys::Reflect::get(&error, &JsValue::from_str("name")).unwrap_or(JsValue::NULL);
let name = name_js_value.as_string().unwrap_or_default();
name == #export_as_identifier_str
}

#[automatically_derived]
impl From<#type_identifier> for JsValue {
fn from(error: #type_identifier) -> Self {
let js_error = SdkJsError::new(error.to_string());
js_error.set_name(#export_as_identifier_str.to_owned());
js_error.set_variant(error.error_variant().to_owned());
js_error.into()
}
}
};
}
}
1 change: 1 addition & 0 deletions crates/bitwarden-error-macro/src/flat/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod attribute;
28 changes: 28 additions & 0 deletions crates/bitwarden-error-macro/src/full/attribute.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use darling::Error;
use quote::quote;

pub(crate) fn bitwarden_error_full(
input: &syn::DeriveInput,
type_identifier: &proc_macro2::Ident,
export_as_identifier: &proc_macro2::Ident,
) -> proc_macro::TokenStream {
if type_identifier != export_as_identifier {
return Error::custom("`bitwarden_error(full)` does not currently support `export_as`")
.write_errors()
.into();
}

let wasm_attributes = cfg!(feature = "wasm").then(|| {
quote! {
#[derive(bitwarden_error::tsify_next::Tsify)]
#[tsify(into_wasm_abi)]
}
});

quote! {
#[derive(serde::Serialize)]
#wasm_attributes
#input
}
.into()
}
1 change: 1 addition & 0 deletions crates/bitwarden-error-macro/src/full/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod attribute;
Loading

0 comments on commit cc34372

Please sign in to comment.