Skip to content

Commit

Permalink
[PM-8301] Create bitwarden-core with macro and uniffi (#824)
Browse files Browse the repository at this point in the history
Extract the `require` macro and common uniffi logic to a new crate
called `bitwarden-core` since it's required for extracting other
bitwarden functionality.
  • Loading branch information
Hinton authored Jun 10, 2024
1 parent 85ac7c4 commit 5ee45a1
Show file tree
Hide file tree
Showing 40 changed files with 190 additions and 100 deletions.
11 changes: 11 additions & 0 deletions .github/workflows/publish-rust-crates.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ on:
required: true
default: true
type: boolean
publish_bitwarden-core:
description: "Publish bitwarden-core crate"
required: true
default: true
type: boolean
publish_bitwarden-crypto:
description: "Publish bitwarden-crypto crate"
required: true
Expand Down Expand Up @@ -81,6 +86,7 @@ jobs:
PUBLISH_BITWARDEN: ${{ github.event.inputs.publish_bitwarden }}
PUBLISH_BITWARDEN_API_API: ${{ github.event.inputs.publish_bitwarden-api-api }}
PUBLISH_BITWARDEN_API_IDENTITY: ${{ github.event.inputs.publish_bitwarden-api-identity }}
PUBLISH_BITWARDEN_CORE: ${{ github.event.inputs.publish_bitwarden-core }}
PUBLISH_BITWARDEN_CRYPTO: ${{ github.event.inputs.publish_bitwarden-crypto }}
PUBLISH_BITWARDEN_CLI: ${{ github.event.inputs.publish_bitwarden-cli }}
PUBLISH_BITWARDEN_GENERATORS: ${{ github.event.inputs.publish_bitwarden-generators }}
Expand Down Expand Up @@ -111,6 +117,11 @@ jobs:
PACKAGES_LIST="$PACKAGES_LIST bitwarden-api-identity"
fi
if [[ "$PUBLISH_BITWARDEN_CORE" == "true" ]]; then
PACKAGES_COMMAND="$PACKAGES_COMMAND -p bitwarden-core"
PACKAGES_LIST="$PACKAGES_LIST bitwarden-core"
fi
if [[ "$PUBLISH_BITWARDEN_CRYPTO" == "true" ]]; then
PACKAGES_COMMAND="$PACKAGES_COMMAND -p bitwarden-crypto"
PACKAGES_LIST="$PACKAGES_LIST bitwarden-crypto"
Expand Down
14 changes: 13 additions & 1 deletion Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ bitwarden = { path = "crates/bitwarden", version = "=0.5.0" }
bitwarden-api-api = { path = "crates/bitwarden-api-api", version = "=0.5.0" }
bitwarden-api-identity = { path = "crates/bitwarden-api-identity", version = "=0.5.0" }
bitwarden-cli = { path = "crates/bitwarden-cli", version = "=0.5.0" }
bitwarden-core = { path = "crates/bitwarden-core", version = "=0.5.0" }
bitwarden-crypto = { path = "crates/bitwarden-crypto", version = "=0.5.0" }
bitwarden-exporters = { path = "crates/bitwarden-exporters", version = "=0.5.0" }
bitwarden-generators = { path = "crates/bitwarden-generators", version = "=0.5.0" }
Expand Down
26 changes: 26 additions & 0 deletions crates/bitwarden-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "bitwarden-core"
description = """
Internal crate for the bitwarden crate. Do not use.
"""

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]
uniffi = ["dep:uniffi"]

[dependencies]
chrono = { version = ">=0.4.26, <0.5", default-features = false }
uniffi = { version = "=0.27.2", optional = true }
uuid = { version = ">=1.3.3, <2.0", features = ["serde"] }
thiserror = ">=1.0.40, <2.0"

[lints]
workspace = true
19 changes: 19 additions & 0 deletions crates/bitwarden-core/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use thiserror::Error;

#[derive(Debug, Error)]
#[error("The response received was missing a required field: {0}")]
pub struct MissingFieldError(pub &'static str);

/// This macro is used to require that a value is present or return an error otherwise.
/// It is equivalent to using `val.ok_or(Error::MissingFields)?`, but easier to use and
/// with a more descriptive error message.
/// Note that this macro will return early from the function if the value is not present.
#[macro_export]
macro_rules! require {
($val:expr) => {
match $val {
Some(val) => val,
None => return Err($crate::MissingFieldError(stringify!($val)).into()),
}
};
}
7 changes: 7 additions & 0 deletions crates/bitwarden-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#[cfg(feature = "uniffi")]
uniffi::setup_scaffolding!();
#[cfg(feature = "uniffi")]
mod uniffi_support;

mod error;
pub use error::MissingFieldError;
41 changes: 41 additions & 0 deletions crates/bitwarden-core/src/uniffi_support.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use uuid::Uuid;

use crate::UniffiCustomTypeConverter;

type DateTime = chrono::DateTime<chrono::Utc>;
uniffi::custom_type!(DateTime, std::time::SystemTime);

impl UniffiCustomTypeConverter for chrono::DateTime<chrono::Utc> {
type Builtin = std::time::SystemTime;

fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
Ok(Self::from(val))
}

fn from_custom(obj: Self) -> Self::Builtin {
obj.into()
}
}

uniffi::custom_type!(Uuid, String);

impl UniffiCustomTypeConverter for Uuid {
type Builtin = String;

fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
Uuid::parse_str(val.as_str()).map_err(|e| e.into())
}

fn from_custom(obj: Self) -> Self::Builtin {
obj.to_string()
}
}

// Uniffi doesn't emit unused types, this is a dummy record to ensure that the custom type
// converters are emitted
#[allow(dead_code)]
#[derive(uniffi::Record)]
struct UniffiConverterDummyRecord {
uuid: Uuid,
date: DateTime,
}
9 changes: 9 additions & 0 deletions crates/bitwarden-core/uniffi.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[bindings.kotlin]
package_name = "com.bitwarden.core"
generate_immutable_records = true
android = true

[bindings.swift]
ffi_module_name = "BitwardenCoreFFI"
module_name = "BitwardenCore"
generate_immutable_records = true
1 change: 0 additions & 1 deletion crates/bitwarden-crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ default = []

uniffi = ["dep:uniffi"] # Uniffi bindings
no-memory-hardening = [] # Disable memory hardening features
test = [] # Test methods

[dependencies]
aes = { version = ">=0.8.2, <0.9", features = ["zeroize"] }
Expand Down
3 changes: 0 additions & 3 deletions crates/bitwarden-exporters/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,5 @@ serde_json = ">=1.0.96, <2.0"
thiserror = ">=1.0.40, <2.0"
uuid = { version = ">=1.3.3, <2.0", features = ["serde", "v4"] }

[dev-dependencies]
bitwarden-crypto = { workspace = true, features = ["test"] }

[lints]
workspace = true
1 change: 1 addition & 0 deletions crates/bitwarden-uniffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ bench = false
async-lock = "3.3.0"
async-trait = "0.1.80"
bitwarden = { workspace = true, features = ["internal", "uniffi"] }
bitwarden-core = { workspace = true, features = ["uniffi"] }
bitwarden-crypto = { workspace = true, features = ["uniffi"] }
bitwarden-generators = { workspace = true, features = ["uniffi"] }
chrono = { version = ">=0.4.26, <0.5", features = [
Expand Down
4 changes: 2 additions & 2 deletions crates/bitwarden-uniffi/src/uniffi_support.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use uuid::Uuid;

// Forward the type definitions to the main bitwarden crate
type DateTime = chrono::DateTime<chrono::Utc>;
uniffi::ffi_converter_forward!(DateTime, bitwarden::UniFfiTag, crate::UniFfiTag);
uniffi::ffi_converter_forward!(DateTime, bitwarden_core::UniFfiTag, crate::UniFfiTag);
uniffi::ffi_converter_forward!(EncString, bitwarden::UniFfiTag, crate::UniFfiTag);
uniffi::ffi_converter_forward!(AsymmetricEncString, bitwarden::UniFfiTag, crate::UniFfiTag);
uniffi::ffi_converter_forward!(Uuid, bitwarden::UniFfiTag, crate::UniFfiTag);
uniffi::ffi_converter_forward!(Uuid, bitwarden_core::UniFfiTag, crate::UniFfiTag);
3 changes: 2 additions & 1 deletion crates/bitwarden/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ no-memory-hardening = [
"bitwarden-crypto/no-memory-hardening",
] # Disable memory hardening features
uniffi = [
"bitwarden-core/uniffi",
"bitwarden-crypto/uniffi",
"bitwarden-generators/uniffi",
"dep:uniffi",
Expand All @@ -39,6 +40,7 @@ async-trait = ">=0.1.80, <0.2"
base64 = ">=0.21.2, <0.23"
bitwarden-api-api = { workspace = true }
bitwarden-api-identity = { workspace = true }
bitwarden-core = { workspace = true }
bitwarden-crypto = { workspace = true }
bitwarden-exporters = { workspace = true, optional = true }
bitwarden-generators = { workspace = true, optional = true }
Expand Down Expand Up @@ -95,7 +97,6 @@ reqwest = { version = ">=0.12, <0.13", features = [
security-framework = { version = "=2.10" }

[dev-dependencies]
bitwarden-crypto = { workspace = true, features = ["test"] }
rand_chacha = "0.3.1"
tokio = { version = "1.36.0", features = ["rt", "macros"] }
wiremock = "0.6.0"
Expand Down
3 changes: 2 additions & 1 deletion crates/bitwarden/src/admin_console/policy.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use std::collections::HashMap;

use bitwarden_api_api::models::PolicyResponseModel;
use bitwarden_core::require;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use uuid::Uuid;

use crate::error::{require, Error, Result};
use crate::error::{Error, Result};

#[derive(Serialize, Deserialize, Debug, JsonSchema)]
pub struct Policy {
Expand Down
3 changes: 2 additions & 1 deletion crates/bitwarden/src/auth/login/access_token.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::path::{Path, PathBuf};

use base64::{engine::general_purpose::STANDARD, Engine};
use bitwarden_core::require;
use bitwarden_crypto::{EncString, KeyDecryptable, SymmetricCryptoKey};
use chrono::Utc;
use schemars::JsonSchema;
Expand All @@ -14,7 +15,7 @@ use crate::{
AccessToken, JWTToken,
},
client::{LoginMethod, ServiceAccountLoginMethod},
error::{require, Error, Result},
error::{Error, Result},
secrets_manager::state::{self, ClientState},
Client,
};
Expand Down
3 changes: 2 additions & 1 deletion crates/bitwarden/src/auth/login/api_key.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use bitwarden_core::require;
use bitwarden_crypto::{EncString, MasterKey};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
Expand All @@ -9,7 +10,7 @@ use crate::{
JWTToken,
},
client::{LoginMethod, UserLoginMethod},
error::{require, Result},
error::Result,
Client,
};

Expand Down
3 changes: 2 additions & 1 deletion crates/bitwarden/src/auth/login/auth_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use bitwarden_api_api::{
apis::auth_requests_api::{auth_requests_id_response_get, auth_requests_post},
models::{AuthRequestCreateRequestModel, AuthRequestType},
};
use bitwarden_core::require;
use bitwarden_crypto::Kdf;
use uuid::Uuid;

Expand All @@ -11,7 +12,7 @@ use crate::{
auth_request::new_auth_request,
},
client::{LoginMethod, UserLoginMethod},
error::{require, Result},
error::Result,
mobile::crypto::{AuthRequestMethod, InitUserCryptoMethod, InitUserCryptoRequest},
Client,
};
Expand Down
3 changes: 2 additions & 1 deletion crates/bitwarden/src/auth/login/password.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ pub(crate) async fn login_password(
client: &mut Client,
input: &PasswordLoginRequest,
) -> Result<PasswordLoginResponse> {
use bitwarden_core::require;
use bitwarden_crypto::{EncString, HashPurpose, MasterKey};

use crate::{client::UserLoginMethod, error::require};
use crate::client::UserLoginMethod;

info!("password logging in");

Expand Down
18 changes: 2 additions & 16 deletions crates/bitwarden/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use thiserror::Error;

#[derive(Debug, Error)]
pub enum Error {
#[error(transparent)]
MissingFieldError(#[from] bitwarden_core::MissingFieldError),
#[error("The client is not authenticated or the session has expired")]
NotAuthenticated,

Expand All @@ -26,8 +28,6 @@ pub enum Error {

#[error("The response received was invalid and could not be processed")]
InvalidResponse,
#[error("The response received was missing some of the required fields: {0}")]
MissingFields(&'static str),

#[error("Cryptography error, {0}")]
Crypto(#[from] bitwarden_crypto::CryptoError),
Expand Down Expand Up @@ -154,18 +154,4 @@ macro_rules! impl_bitwarden_error {
impl_bitwarden_error!(ApiError);
impl_bitwarden_error!(IdentityError);

/// This macro is used to require that a value is present or return an error otherwise.
/// It is equivalent to using `val.ok_or(Error::MissingFields)?`, but easier to use and
/// with a more descriptive error message.
/// Note that this macro will return early from the function if the value is not present.
macro_rules! require {
($val:expr) => {
match $val {
Some(val) => val,
None => return Err($crate::error::Error::MissingFields(stringify!($val))),
}
};
}
pub(crate) use require;

pub type Result<T, E = Error> = std::result::Result<T, E>;
3 changes: 2 additions & 1 deletion crates/bitwarden/src/platform/get_user_api_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use bitwarden_api_api::{
apis::accounts_api::accounts_api_key_post,
models::{ApiKeyResponseModel, SecretVerificationRequestModel},
};
use bitwarden_core::require;
use bitwarden_crypto::{HashPurpose, MasterKey};
use log::{debug, info};
use schemars::JsonSchema;
Expand All @@ -10,7 +11,7 @@ use serde::{Deserialize, Serialize};
use super::SecretVerificationRequest;
use crate::{
client::{LoginMethod, UserLoginMethod},
error::{require, Error, Result},
error::{Error, Result},
Client,
};

Expand Down
6 changes: 2 additions & 4 deletions crates/bitwarden/src/secrets_manager/projects/delete.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
use bitwarden_api_api::models::{
BulkDeleteResponseModel, BulkDeleteResponseModelListResponseModel,
};
use bitwarden_core::require;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

use crate::{
client::Client,
error::{require, Result},
};
use crate::{client::Client, error::Result};

#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
Expand Down
Loading

0 comments on commit 5ee45a1

Please sign in to comment.