diff --git a/Cargo.lock b/Cargo.lock index 7dd4b4195..4cfe189b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -543,6 +543,19 @@ dependencies = [ "wiremock", ] +[[package]] +name = "bitwarden-jobs" +version = "1.0.0" +dependencies = [ + "js-sys", + "serde", + "trybuild", + "tsify-next", + "uuid", + "wasm-bindgen", + "wasm-bindgen-test", +] + [[package]] name = "bitwarden-send" version = "1.0.0" diff --git a/Cargo.toml b/Cargo.toml index 64b9ba374..bfcdf7c18 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ bitwarden-crypto = { path = "crates/bitwarden-crypto", version = "=1.0.0" } bitwarden-exporters = { path = "crates/bitwarden-exporters", version = "=1.0.0" } bitwarden-fido = { path = "crates/bitwarden-fido", version = "=1.0.0" } bitwarden-generators = { path = "crates/bitwarden-generators", version = "=1.0.0" } +bitwarden-jobs = { path = "crates/bitwarden-jobs", 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-ssh = { path = "crates/bitwarden-ssh", version = "=1.0.0" } diff --git a/crates/bitwarden-jobs/.cargo/config b/crates/bitwarden-jobs/.cargo/config new file mode 100644 index 000000000..4ec2f3b86 --- /dev/null +++ b/crates/bitwarden-jobs/.cargo/config @@ -0,0 +1,2 @@ +[target.wasm32-unknown-unknown] +runner = 'wasm-bindgen-test-runner' diff --git a/crates/bitwarden-jobs/Cargo.toml b/crates/bitwarden-jobs/Cargo.toml new file mode 100644 index 000000000..0a2d1c431 --- /dev/null +++ b/crates/bitwarden-jobs/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "bitwarden-jobs" +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 = ["dep:js-sys", "dep:tsify-next", "dep:wasm-bindgen"] + +[dependencies] +js-sys = { workspace = true, optional = true } +tsify-next = { workspace = true, optional = true } +uuid = { workspace = true } +wasm-bindgen = { workspace = true, optional = true } + +[lints] +workspace = true + +[dev-dependencies] +serde.workspace = true +trybuild = "1.0.101" +wasm-bindgen-test = "0.3.45" diff --git a/crates/bitwarden-jobs/src/job.rs b/crates/bitwarden-jobs/src/job.rs new file mode 100644 index 000000000..c03422dea --- /dev/null +++ b/crates/bitwarden-jobs/src/job.rs @@ -0,0 +1,57 @@ +use std::future::Future; + +pub struct JobInformation { + pub id: uuid::Uuid, + pub name: String, + pub description: String, + pub trigger: JobTrigger, + pub concurrency: JobConcurrencyProtections, + pub enabled: bool, +} + +pub enum JobTrigger { + Interval(String), + Event(JobEventTrigger), + Manual, +} + +pub enum JobEventTrigger { + /// Triggered whenever the vault is unlocked and all keys are available. + VaultUnlock, + /// Triggered whenever the vault is synced. + VaultSynced, + /// Triggered whenever the vault is synced and decryptable. + VaultSyncedUnlocked, + /// Triggered whenever a user enters the authenticated state. This can be used either after a successfull login + /// or after a session is restored when the application restarts. + UserAuthenticated, + /// Triggered whenever a user is about to be logged out. The job will block the logout until it is finished, + /// so be careful with long running jobs. + UserLogout, +} + +pub enum JobConcurrencyProtections { + /// The job will not run if another instance of the job is already running on another device. + /// from running on other devices. This guarantee can only be made if the device is online, meaning that + /// this job will not run if the current device is offline. + /// + /// Note that concurrency protection might no be implemented on a per-job basis, meaning that + /// the job might not run even if the trigger is fired and no other device is running it. For example, + /// the system might assign a "current job runner" to a device and only allow that device to run the job. + /// This means that the job will not run on other devices even if the trigger is fired on them. + SingleInstancePerUser, + /// The job will not run if another instance of the job is already running on the current device. + /// Other devices might run the job at the same time. This job will run even if the device is offline. + SingleInstancePerDevice, + /// The job will always run when triggered. + None, +} + +pub enum JobRunError { + JobFailed, +} + +pub trait Job { + fn get_information(&self) -> JobInformation; + fn run(&self) -> impl Future>; +} diff --git a/crates/bitwarden-jobs/src/job_client.rs b/crates/bitwarden-jobs/src/job_client.rs new file mode 100644 index 000000000..4ed1a34ab --- /dev/null +++ b/crates/bitwarden-jobs/src/job_client.rs @@ -0,0 +1,5 @@ +use crate::job::Job; + +pub struct JobClient { + jobs: Vec>, +} diff --git a/crates/bitwarden-jobs/src/lib.rs b/crates/bitwarden-jobs/src/lib.rs new file mode 100644 index 000000000..ac36e5b1f --- /dev/null +++ b/crates/bitwarden-jobs/src/lib.rs @@ -0,0 +1,2 @@ +mod job; +mod job_client;