From 1cb98557c9617f537ae78a603ff5b50a2ba6ebb6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 11:22:31 +0100 Subject: [PATCH 01/34] [deps]: Update Rust crate env_logger to v0.11.1 (#547) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [env_logger](https://togithub.com/rust-cli/env_logger) | dependencies | minor | `0.10.1` -> `0.11.1` | --- ### Release Notes
rust-cli/env_logger (env_logger) ### [`v0.11.1`](https://togithub.com/rust-cli/env_logger/blob/HEAD/CHANGELOG.md#0111---2024-01-27) [Compare Source](https://togithub.com/rust-cli/env_logger/compare/v0.11.0...v0.11.1) ##### Fixes - Allow styling with `Target::Pipe` ### [`v0.11.0`](https://togithub.com/rust-cli/env_logger/blob/HEAD/CHANGELOG.md#0110---2024-01-19) [Compare Source](https://togithub.com/rust-cli/env_logger/compare/v0.10.2...v0.11.0) ##### Breaking Change - Removed bespoke styling API - `env_logger::fmt::Formatter::style` - `env_logger::fmt::Formatter::default_styled_level` - `env_logger::fmt::Style` - `env_logger::fmt::Color` - `env_logger::fmt::StyledValue` - Removed `env_logger::filter` in favor of `env_filter` ##### Compatibility MSRV changed to 1.71 ##### Features - Automatically adapt ANSI escape codes in logged messages to the current terminal's capabilities - Add support for `NO_COLOR` and `CLICOLOR_FORCE`, see https://bixense.com/clicolors/ ##### Fixes - Print colors when `is_test(true)` - Allow styling with `Target::Pipe`
--- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/bitwarden/sdk). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Cargo.lock | 29 +++++++++++++++-------------- crates/bitwarden-c/Cargo.toml | 2 +- crates/bitwarden-napi/Cargo.toml | 2 +- crates/bitwarden-uniffi/Cargo.toml | 2 +- crates/bw/Cargo.toml | 2 +- crates/bws/Cargo.toml | 2 +- 6 files changed, 20 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7dc7828c5..52bc0239b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1173,17 +1173,27 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "env_filter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +dependencies = [ + "log", + "regex", +] + [[package]] name = "env_logger" -version = "0.10.2" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +checksum = "05e7cf40684ae96ade6232ed84582f40ce0a66efcd43a5117aef610534f8e0b8" dependencies = [ + "anstream", + "anstyle", + "env_filter", "humantime", - "is-terminal", "log", - "regex", - "termcolor", ] [[package]] @@ -3293,15 +3303,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] - [[package]] name = "textwrap" version = "0.16.0" diff --git a/crates/bitwarden-c/Cargo.toml b/crates/bitwarden-c/Cargo.toml index 20f3f5229..c6f0ec7f3 100644 --- a/crates/bitwarden-c/Cargo.toml +++ b/crates/bitwarden-c/Cargo.toml @@ -14,4 +14,4 @@ tokio = { version = ">=1.28.2, <2.0", features = ["rt-multi-thread", "macros"] } bitwarden-json = { path = "../bitwarden-json", features = ["secrets"] } [dependencies] -env_logger = ">=0.10.0, <0.11" +env_logger = ">=0.10.0, <0.12" diff --git a/crates/bitwarden-napi/Cargo.toml b/crates/bitwarden-napi/Cargo.toml index 63eae2b8b..4b6ff4ed1 100644 --- a/crates/bitwarden-napi/Cargo.toml +++ b/crates/bitwarden-napi/Cargo.toml @@ -16,7 +16,7 @@ rust-version = "1.57" crate-type = ["cdylib", "rlib"] [dependencies] -env_logger = "0.10.1" +env_logger = "0.11.1" log = "0.4.20" napi = { version = "2", features = ["async"] } napi-derive = "2" diff --git a/crates/bitwarden-uniffi/Cargo.toml b/crates/bitwarden-uniffi/Cargo.toml index 59db0daf7..7f780558b 100644 --- a/crates/bitwarden-uniffi/Cargo.toml +++ b/crates/bitwarden-uniffi/Cargo.toml @@ -17,7 +17,7 @@ chrono = { version = ">=0.4.26, <0.5", features = [ "serde", "std", ], default-features = false } -env_logger = "0.10.1" +env_logger = "0.11.1" schemars = { version = ">=0.8, <0.9", optional = true } uniffi = "=0.26.1" diff --git a/crates/bw/Cargo.toml b/crates/bw/Cargo.toml index 210a6b80a..cc07397cd 100644 --- a/crates/bw/Cargo.toml +++ b/crates/bw/Cargo.toml @@ -15,7 +15,7 @@ keywords = ["bitwarden", "password-manager", "cli"] [dependencies] clap = { version = "4.4.18", features = ["derive", "env"] } color-eyre = "0.6" -env_logger = "0.10.1" +env_logger = "0.11.1" inquire = "0.6.2" log = "0.4.20" tokio = { version = "1.35.1", features = ["rt-multi-thread", "macros"] } diff --git a/crates/bws/Cargo.toml b/crates/bws/Cargo.toml index 30163d00b..8d821f9d3 100644 --- a/crates/bws/Cargo.toml +++ b/crates/bws/Cargo.toml @@ -25,7 +25,7 @@ clap_complete = "4.4.9" color-eyre = "0.6" comfy-table = "^7.1.0" directories = "5.0.1" -env_logger = "0.10.1" +env_logger = "0.11.1" log = "0.4.20" regex = { version = "1.10.3", features = [ "std", From 51ae8bf3acd412d693968d2800d9038d6bf45e67 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 12:01:08 +0100 Subject: [PATCH 02/34] [deps]: Update Rust crate uuid to ^1.7.0 (#546) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [uuid](https://togithub.com/uuid-rs/uuid) | dependencies | patch | `^1.6.1` -> `^1.7.0` | --- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/bitwarden/sdk). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- crates/bws/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bws/Cargo.toml b/crates/bws/Cargo.toml index 8d821f9d3..1c04a6668 100644 --- a/crates/bws/Cargo.toml +++ b/crates/bws/Cargo.toml @@ -38,7 +38,7 @@ supports-color = "2.1.0" thiserror = "1.0.56" tokio = { version = "1.35.1", features = ["rt-multi-thread", "macros"] } toml = "0.8.8" -uuid = { version = "^1.6.1", features = ["serde"] } +uuid = { version = "^1.7.0", features = ["serde"] } bitwarden = { path = "../bitwarden", version = "0.4.0", features = ["secrets"] } From 2a30f0fe2deafabea6bdfb6cf178dc0288a3e345 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 12:01:24 +0100 Subject: [PATCH 03/34] [deps]: Update @types/node to v18.19.10 (#542) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [@types/node](https://togithub.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node) ([source](https://togithub.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node)) | [`18.19.9` -> `18.19.10`](https://renovatebot.com/diffs/npm/@types%2fnode/18.19.9/18.19.10) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@types%2fnode/18.19.10?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@types%2fnode/18.19.10?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@types%2fnode/18.19.9/18.19.10?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@types%2fnode/18.19.9/18.19.10?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/bitwarden/sdk). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- languages/js/sdk-client/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/languages/js/sdk-client/package-lock.json b/languages/js/sdk-client/package-lock.json index 4f2b52824..004be9f0e 100644 --- a/languages/js/sdk-client/package-lock.json +++ b/languages/js/sdk-client/package-lock.json @@ -39,9 +39,9 @@ } }, "node_modules/@types/node": { - "version": "18.19.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.9.tgz", - "integrity": "sha512-oZFKlC8l5YtzGQNT4zC2PiSSKzQVZ8bAwwd+EYdPLtyk0nSEq6O16SkK+rkkT2eflDAbormJgEF3QnH3oDrTSw==", + "version": "18.19.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.10.tgz", + "integrity": "sha512-IZD8kAM02AW1HRDTPOlz3npFava678pr8Ie9Vp8uRhBROXAv8MXT2pCnGZZAKYdromsNQLHQcfWQ6EOatVLtqA==", "dev": true, "dependencies": { "undici-types": "~5.26.4" From 7dacc63c4403d88fbc401674fcb3b83b49c4bded Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 13:17:04 +0100 Subject: [PATCH 04/34] [deps]: Lock file maintenance (#557) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Update | Change | |---|---| | lockFileMaintenance | All locks refreshed | 🔧 This Pull Request updates lock files to use the latest dependency versions. --- ### Configuration 📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://togithub.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/bitwarden/sdk). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Cargo.lock | 36 +++++++++++------------ crates/bitwarden-napi/package-lock.json | 6 ++-- languages/js/sdk-client/package-lock.json | 6 ++-- package-lock.json | 18 ++++++------ 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 52bc0239b..c4e1fe3c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1501,7 +1501,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.1.0", + "indexmap 2.2.1", "slab", "tokio", "tokio-util", @@ -1716,9 +1716,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.1.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "433de089bd45971eecf4668ee0ee8f4cec17db4f8bd8f7bc3197a6ce37aa7d9b" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -1980,9 +1980,9 @@ dependencies = [ [[package]] name = "napi" -version = "2.14.4" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "902495f6b80f53f8435aefbbd2241c9c675fa239cd7e5f8e28fb57f3b69ecd09" +checksum = "efbf98e1bcb85cc441bbf7cdfb11070d2537a100e2697d75397b2584c32492d1" dependencies = [ "bitflags 2.4.2", "ctor", @@ -2000,9 +2000,9 @@ checksum = "d4b4532cf86bfef556348ac65e561e3123879f0e7566cca6d43a6ff5326f13df" [[package]] name = "napi-derive" -version = "2.14.6" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e61bec1ee990ae3e9a5f443484c65fb38e571a898437f0ad283ed69c82fc59c0" +checksum = "7622f0dbe0968af2dacdd64870eee6dee94f93c989c841f1ad8f300cf1abd514" dependencies = [ "cfg-if", "convert_case", @@ -2014,9 +2014,9 @@ dependencies = [ [[package]] name = "napi-derive-backend" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2314f777bc9cde51705d991c44466cee4de4a3f41c6d3d019fcbbebb5cdd47c4" +checksum = "8ec514d65fce18a959be55e7f683ac89c6cb850fb59b09e25ab777fd5a4a8d9e" dependencies = [ "convert_case", "once_cell", @@ -2325,7 +2325,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5699cc8a63d1aa2b1ee8e12b9ad70ac790d65788cd36101fa37f87ea46c4cef" dependencies = [ "base64 0.21.7", - "indexmap 2.1.0", + "indexmap 2.2.1", "line-wrap", "quick-xml", "serde", @@ -2579,9 +2579,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" dependencies = [ "aho-corasick", "memchr", @@ -3028,11 +3028,11 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.30" +version = "0.9.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1bf28c79a99f70ee1f1d83d10c875d2e70618417fda01ad1785e027579d9d38" +checksum = "adf8a49373e98a4c5f0ceb5d05aa7c648d75f63774981ed95b7c7443bbd50c6e" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.1", "itoa", "ryu", "serde", @@ -3476,7 +3476,7 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.1", "serde", "serde_spanned", "toml_datetime", @@ -4118,9 +4118,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.34" +version = "0.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" +checksum = "1931d78a9c73861da0134f453bb1f790ce49b2e30eba8410b4b79bac72b46a2d" dependencies = [ "memchr", ] diff --git a/crates/bitwarden-napi/package-lock.json b/crates/bitwarden-napi/package-lock.json index 6d1288fa9..a8995bc77 100644 --- a/crates/bitwarden-napi/package-lock.json +++ b/crates/bitwarden-napi/package-lock.json @@ -95,9 +95,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.11.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.6.tgz", - "integrity": "sha512-+EOokTnksGVgip2PbYbr3xnR7kZigh4LbybAfBAw5BpnQ+FqBYUsvCEjYd70IXKlbohQ64mzEYmMtlWUY8q//Q==", + "version": "20.11.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.10.tgz", + "integrity": "sha512-rZEfe/hJSGYmdfX9tvcPMYeYPW2sNl50nsw4jZmRcaG0HIAb0WYEpsB05GOb53vjqpyE9GUhlDQ4jLSoB5q9kg==", "dev": true, "peer": true, "dependencies": { diff --git a/languages/js/sdk-client/package-lock.json b/languages/js/sdk-client/package-lock.json index 004be9f0e..d1192e6f5 100644 --- a/languages/js/sdk-client/package-lock.json +++ b/languages/js/sdk-client/package-lock.json @@ -202,9 +202,9 @@ } }, "node_modules/lru-cache": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", - "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", "dev": true, "engines": { "node": "14 || >=16.14" diff --git a/package-lock.json b/package-lock.json index f7c429825..747d7d809 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,9 +19,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.23.8", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.8.tgz", - "integrity": "sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", + "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" @@ -346,9 +346,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.11.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.6.tgz", - "integrity": "sha512-+EOokTnksGVgip2PbYbr3xnR7kZigh4LbybAfBAw5BpnQ+FqBYUsvCEjYd70IXKlbohQ64mzEYmMtlWUY8q//Q==", + "version": "20.11.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.10.tgz", + "integrity": "sha512-rZEfe/hJSGYmdfX9tvcPMYeYPW2sNl50nsw4jZmRcaG0HIAb0WYEpsB05GOb53vjqpyE9GUhlDQ4jLSoB5q9kg==", "dev": true, "peer": true, "dependencies": { @@ -1253,9 +1253,9 @@ } }, "node_modules/lru-cache": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", - "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", "dev": true, "engines": { "node": "14 || >=16.14" From eed8d568283658e107085014a04f06a9357de5e6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 10:32:17 -0500 Subject: [PATCH 05/34] [deps]: Update docker/setup-qemu-action action to v3 (#556) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build-cli-docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-cli-docker.yml b/.github/workflows/build-cli-docker.yml index 4605da5e5..dd70fe51d 100644 --- a/.github/workflows/build-cli-docker.yml +++ b/.github/workflows/build-cli-docker.yml @@ -44,7 +44,7 @@ jobs: ########## Set up Docker ########## - name: Set up QEMU emulators - uses: docker/setup-qemu-action@2b82ce82d56a2a04d2637cd93a637ae1b359c0a7 # v2.2.0 + uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0 - name: Set up Docker Buildx uses: docker/setup-buildx-action@885d1462b80bc1c1c7f0b00334ad271f09369c55 # v2.10.0 From 218a9c828beb7669f0c1ca50dcaa31e2da2531d5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 10:36:04 -0500 Subject: [PATCH 06/34] [deps]: Update docker/setup-buildx-action action to v3 (#555) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build-cli-docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-cli-docker.yml b/.github/workflows/build-cli-docker.yml index dd70fe51d..9b1960a44 100644 --- a/.github/workflows/build-cli-docker.yml +++ b/.github/workflows/build-cli-docker.yml @@ -47,7 +47,7 @@ jobs: uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@885d1462b80bc1c1c7f0b00334ad271f09369c55 # v2.10.0 + uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 ########## Login to Docker registries ########## - name: Login to Azure - Prod Subscription From f78a98f452031d295aa9c5e2ffb57f99c0dd312c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 10:38:39 -0500 Subject: [PATCH 07/34] [deps]: Update docker/build-push-action action to v5 (#554) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build-cli-docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-cli-docker.yml b/.github/workflows/build-cli-docker.yml index 9b1960a44..c0aa62664 100644 --- a/.github/workflows/build-cli-docker.yml +++ b/.github/workflows/build-cli-docker.yml @@ -104,7 +104,7 @@ jobs: fi - name: Build and push Docker image - uses: docker/build-push-action@1104d471370f9806843c095c1db02b5a90c5f8b6 # v3.3.1 + uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5.1.0 with: context: . file: crates/bws/Dockerfile From 4aca392a5b61432d237f1e553b00d5dcc16cca9b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 10:39:06 -0500 Subject: [PATCH 08/34] [deps]: Update actions/upload-artifact action to v4 (#553) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build-python-wheels.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-python-wheels.yml b/.github/workflows/build-python-wheels.yml index dace2d047..5dc5b8d23 100644 --- a/.github/workflows/build-python-wheels.yml +++ b/.github/workflows/build-python-wheels.yml @@ -109,14 +109,14 @@ jobs: working-directory: ${{ github.workspace }}/languages/python - name: Upload wheels - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 with: name: bitwarden_sdk-${{ env._PACKAGE_VERSION }}-${{ matrix.settings.target }} path: ${{ github.workspace }}/target/wheels/bitwarden_sdk*.whl - name: Upload sdists if: ${{ matrix.settings.target == 'x86_64-unknown-linux-gnu' }} # we only need one sdist - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 with: name: bitwarden_sdk-${{ env._PACKAGE_VERSION }}-sdist path: ${{ github.workspace }}/target/wheels/bitwarden_sdk-*.tar.gz From 26110b2afab42e08498ba3c775befbda78d03ba5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 10:41:56 -0500 Subject: [PATCH 09/34] [deps]: Update actions/delete-package-versions action to v5 (#552) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/delete-old-packages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/delete-old-packages.yml b/.github/workflows/delete-old-packages.yml index 50c3a8fef..b3be807a6 100644 --- a/.github/workflows/delete-old-packages.yml +++ b/.github/workflows/delete-old-packages.yml @@ -15,7 +15,7 @@ jobs: name: Cleanup Android SDK runs-on: ubuntu-22.04 steps: - - uses: actions/delete-package-versions@0d39a63126868f5eefaa47169615edd3c0f61e20 # v4.1.1 + - uses: actions/delete-package-versions@e5bc658cc4c965c472efe991f8beea3981499c55 # v5.0.0 with: package-name: com.bitwarden.sdk-android package-type: maven From d3026a5972cb52163599bb1205ad2919858e28c9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 10:42:16 -0500 Subject: [PATCH 10/34] [deps]: Update actions/cache action to v4 (#551) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/golang-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/golang-release.yml b/.github/workflows/golang-release.yml index 1578dee57..10ec7675e 100644 --- a/.github/workflows/golang-release.yml +++ b/.github/workflows/golang-release.yml @@ -34,7 +34,7 @@ jobs: go-version: ${{ env.GO_VERSION }} - name: Cache dependencies - uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c # v3.3.3 + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} From 3064ac547093aab08c18fef036e85c3b3bc032ba Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 10:42:40 -0500 Subject: [PATCH 11/34] [deps]: Update gh minor (#549) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build-android.yml | 4 ++-- .github/workflows/build-cli.yml | 6 +++--- .github/workflows/build-dotnet.yml | 2 +- .github/workflows/build-java.yml | 2 +- .github/workflows/build-napi.yml | 2 +- .github/workflows/build-rust-cross-platform.yml | 2 +- .github/workflows/generate_schemas.yml | 14 +++++++------- .github/workflows/publish-ruby.yml | 2 +- .github/workflows/rust-test.yml | 2 +- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build-android.yml b/.github/workflows/build-android.yml index ce08322be..e1aeea244 100644 --- a/.github/workflows/build-android.yml +++ b/.github/workflows/build-android.yml @@ -46,7 +46,7 @@ jobs: run: cross build -p bitwarden-uniffi --release --target=${{ matrix.settings.target }} - name: Upload artifact - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 with: name: android-${{ matrix.settings.target }} path: ./target/${{ matrix.settings.target }}/release/libbitwarden_uniffi.so @@ -102,7 +102,7 @@ jobs: run: ./build-schemas.sh - name: Publish - uses: gradle/gradle-build-action@982da8e78c05368c70dac0351bb82647a9e9a5d2 # v2.11.1 + uses: gradle/gradle-build-action@a8f75513eafdebd8141bd1cd4e30fcd194af8dfa # v2.12.0 with: arguments: sdk:publish build-root-directory: languages/kotlin diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml index 1e8ad2896..769c9bea3 100644 --- a/.github/workflows/build-cli.yml +++ b/.github/workflows/build-cli.yml @@ -99,7 +99,7 @@ jobs: run: zip -j ./bws-${{ matrix.settings.target }}-${{ env._PACKAGE_VERSION }}.zip ./target/${{ matrix.settings.target }}/release/bws - name: Upload artifact - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 with: name: bws-${{ matrix.settings.target }}-${{ env._PACKAGE_VERSION }}.zip path: ./bws-${{ matrix.settings.target }}-${{ env._PACKAGE_VERSION }}.zip @@ -142,7 +142,7 @@ jobs: run: zip ./bws-macos-universal-${{ env._PACKAGE_VERSION }}.zip ./bws-macos-universal/bws - name: Upload artifact - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 with: name: bws-macos-universal-${{ env._PACKAGE_VERSION }}.zip path: ./bws-macos-universal-${{ env._PACKAGE_VERSION }}.zip @@ -177,7 +177,7 @@ jobs: sed -i.bak 's/\$NAME\$/Bitwarden Secrets Manager CLI/g' THIRDPARTY.html - name: Upload artifact - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 with: name: THIRDPARTY.html path: ./crates/bws/THIRDPARTY.html diff --git a/.github/workflows/build-dotnet.yml b/.github/workflows/build-dotnet.yml index 232efee9d..e56bd2282 100644 --- a/.github/workflows/build-dotnet.yml +++ b/.github/workflows/build-dotnet.yml @@ -71,7 +71,7 @@ jobs: working-directory: languages/csharp/Bitwarden.Sdk - name: Upload NuGet package - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 with: name: Bitwarden.Sdk.0.0.1.nupkg path: | diff --git a/.github/workflows/build-java.yml b/.github/workflows/build-java.yml index 3c574d823..0ed8997ca 100644 --- a/.github/workflows/build-java.yml +++ b/.github/workflows/build-java.yml @@ -61,7 +61,7 @@ jobs: path: languages/java/src/main/resources/win32-x86-64 - name: Publish Maven - uses: gradle/gradle-build-action@982da8e78c05368c70dac0351bb82647a9e9a5d2 # v2.11.1 + uses: gradle/gradle-build-action@a8f75513eafdebd8141bd1cd4e30fcd194af8dfa # v2.12.0 with: arguments: publish build-root-directory: languages/java diff --git a/.github/workflows/build-napi.yml b/.github/workflows/build-napi.yml index 40e6ffaef..c1088a220 100644 --- a/.github/workflows/build-napi.yml +++ b/.github/workflows/build-napi.yml @@ -84,7 +84,7 @@ jobs: run: ${{ matrix.settings.build }} - name: Upload artifact - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 with: name: sdk-bitwarden-napi-${{ matrix.settings.target }} path: ${{ github.workspace }}/crates/bitwarden-napi/sdk-napi.*.node diff --git a/.github/workflows/build-rust-cross-platform.yml b/.github/workflows/build-rust-cross-platform.yml index 7495457df..8529ab484 100644 --- a/.github/workflows/build-rust-cross-platform.yml +++ b/.github/workflows/build-rust-cross-platform.yml @@ -41,7 +41,7 @@ jobs: run: cargo build --target ${{ matrix.settings.target }} --release - name: Upload Artifact - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 with: name: libbitwarden_c_files-${{ matrix.settings.target }} path: | diff --git a/.github/workflows/generate_schemas.yml b/.github/workflows/generate_schemas.yml index a18b4910a..99e13089c 100644 --- a/.github/workflows/generate_schemas.yml +++ b/.github/workflows/generate_schemas.yml @@ -37,48 +37,48 @@ jobs: run: npm run schemas - name: Upload ts schemas artifact - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 with: name: schemas.ts path: ${{ github.workspace }}/languages/js/sdk-client/src/schemas.ts if-no-files-found: error - name: Upload c# schemas artifact - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 with: name: schemas.cs path: ${{ github.workspace }}/languages/csharp/Bitwarden.Sdk/schemas.cs if-no-files-found: error - name: Upload python schemas artifact - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 with: name: schemas.py path: ${{ github.workspace }}/languages/python/bitwarden_sdk/schemas.py if-no-files-found: error - name: Upload ruby schemas artifact - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 with: name: schemas.rb path: ${{ github.workspace }}/languages/ruby/bitwarden_sdk/lib/schemas.rb if-no-files-found: error - name: Upload json schemas artifact - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 with: name: sdk-schemas-json path: ${{ github.workspace }}/support/schemas/* if-no-files-found: error - name: Upload Go schemas artifact - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 with: name: schemas.go path: ${{ github.workspace }}/languages/go/schema.go - name: Upload java schemas artifact - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 with: name: sdk-schemas-java path: ${{ github.workspace }}/languages/java/src/main/java/com/bitwarden/sdk/schema/* diff --git a/.github/workflows/publish-ruby.yml b/.github/workflows/publish-ruby.yml index 291834022..81aedcc22 100644 --- a/.github/workflows/publish-ruby.yml +++ b/.github/workflows/publish-ruby.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Set up Ruby - uses: ruby/setup-ruby@5daca165445f0ae10478593083f72ca2625e241d # v1.169.0 + uses: ruby/setup-ruby@bd03e04863f52d169e18a2b190e8fa6b84938215 # v1.170.0 with: ruby-version: 3.2 diff --git a/.github/workflows/rust-test.yml b/.github/workflows/rust-test.yml index 8dfcf1783..93a2ddecf 100644 --- a/.github/workflows/rust-test.yml +++ b/.github/workflows/rust-test.yml @@ -73,7 +73,7 @@ jobs: run: cargo llvm-cov --all-features --lcov --output-path lcov.info --ignore-filename-regex "crates/bitwarden-api-" - name: Upload to codecov.io - uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4 + uses: codecov/codecov-action@4fe8c5f003fae66aa5ebb77cfd3e7bfbbda0b6b0 # v3.1.5 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} From 878cd4a4d3a27c05e3306cf23d5a37920d313400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa?= Date: Wed, 31 Jan 2024 13:49:07 +0100 Subject: [PATCH 12/34] Implement password update support (#566) ## Type of change ``` - [ ] Bug fix - [x] New feature development - [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc) - [ ] Build/deploy pipeline (DevOps) - [ ] Other ``` ## Objective Support re-encrypting the user key with the new password. --- crates/bitwarden-uniffi/src/crypto.rs | 15 ++- crates/bitwarden/src/mobile/client_crypto.rs | 11 +- crates/bitwarden/src/mobile/crypto.rs | 120 +++++++++++++++++++ languages/kotlin/doc.md | 12 ++ 4 files changed, 156 insertions(+), 2 deletions(-) diff --git a/crates/bitwarden-uniffi/src/crypto.rs b/crates/bitwarden-uniffi/src/crypto.rs index 92afcbb87..2d847b33d 100644 --- a/crates/bitwarden-uniffi/src/crypto.rs +++ b/crates/bitwarden-uniffi/src/crypto.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use bitwarden::mobile::crypto::{ - DerivePinKeyResponse, InitOrgCryptoRequest, InitUserCryptoRequest, + DerivePinKeyResponse, InitOrgCryptoRequest, InitUserCryptoRequest, UpdatePasswordResponse, }; use bitwarden_crypto::EncString; @@ -51,6 +51,19 @@ impl ClientCrypto { .await?) } + /// Update the user's password, which will re-encrypt the user's encryption key with the new + /// password. This returns the new encrypted user key and the new password hash. + pub async fn update_password(&self, new_password: String) -> Result { + Ok(self + .0 + .0 + .write() + .await + .crypto() + .update_password(new_password) + .await?) + } + /// Generates a PIN protected user key from the provided PIN. The result can be stored and later /// used to initialize another client instance by using the PIN and the PIN key with /// `initialize_user_crypto`. diff --git a/crates/bitwarden/src/mobile/client_crypto.rs b/crates/bitwarden/src/mobile/client_crypto.rs index 6f8887002..f6ea3346b 100644 --- a/crates/bitwarden/src/mobile/client_crypto.rs +++ b/crates/bitwarden/src/mobile/client_crypto.rs @@ -7,7 +7,8 @@ use crate::{ error::Result, mobile::crypto::{ derive_pin_key, derive_pin_user_key, get_user_encryption_key, initialize_org_crypto, - initialize_user_crypto, DerivePinKeyResponse, InitOrgCryptoRequest, InitUserCryptoRequest, + initialize_user_crypto, update_password, DerivePinKeyResponse, InitOrgCryptoRequest, + InitUserCryptoRequest, UpdatePasswordResponse, }, }; @@ -31,6 +32,14 @@ impl<'a> ClientCrypto<'a> { get_user_encryption_key(self.client).await } + #[cfg(feature = "internal")] + pub async fn update_password( + &mut self, + new_password: String, + ) -> Result { + update_password(self.client, new_password) + } + #[cfg(feature = "internal")] pub async fn derive_pin_key(&mut self, pin: String) -> Result { derive_pin_key(self.client, pin) diff --git a/crates/bitwarden/src/mobile/crypto.rs b/crates/bitwarden/src/mobile/crypto.rs index bd71af2be..9b431b4b4 100644 --- a/crates/bitwarden/src/mobile/crypto.rs +++ b/crates/bitwarden/src/mobile/crypto.rs @@ -125,6 +125,53 @@ pub async fn get_user_encryption_key(client: &mut Client) -> Result { Ok(user_key.to_base64()) } +#[cfg(feature = "internal")] +#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +#[cfg_attr(feature = "mobile", derive(uniffi::Record))] +pub struct UpdatePasswordResponse { + /// Hash of the new password + password_hash: String, + /// User key, encrypted with the new password + new_key: EncString, +} + +pub fn update_password( + client: &mut Client, + new_password: String, +) -> Result { + let user_key = client + .get_encryption_settings()? + .get_key(&None) + .ok_or(Error::VaultLocked)?; + + let login_method = client + .login_method + .as_ref() + .ok_or(Error::NotAuthenticated)?; + + // Derive a new master key from password + let new_master_key = match login_method { + LoginMethod::User( + UserLoginMethod::Username { email, kdf, .. } + | UserLoginMethod::ApiKey { email, kdf, .. }, + ) => MasterKey::derive(new_password.as_bytes(), email.as_bytes(), kdf)?, + _ => return Err(Error::NotAuthenticated), + }; + + let new_key = new_master_key.encrypt_user_key(user_key)?; + + let password_hash = new_master_key.derive_master_key_hash( + new_password.as_bytes(), + bitwarden_crypto::HashPurpose::ServerAuthorization, + )?; + + Ok(UpdatePasswordResponse { + password_hash, + new_key, + }) +} + #[cfg(feature = "internal")] #[derive(Serialize, Deserialize, Debug, JsonSchema)] #[serde(rename_all = "camelCase", deny_unknown_fields)] @@ -194,6 +241,79 @@ mod tests { use super::*; use crate::{client::Kdf, Client}; + #[tokio::test] + async fn test_update_password() { + let mut client = Client::new(None); + + let priv_key = "2.kmLY8NJVuiKBFJtNd/ZFpA==|qOodlRXER+9ogCe3yOibRHmUcSNvjSKhdDuztLlucs10jLiNoVVVAc+9KfNErLSpx5wmUF1hBOJM8zwVPjgQTrmnNf/wuDpwiaCxNYb/0v4FygPy7ccAHK94xP1lfqq7U9+tv+/yiZSwgcT+xF0wFpoxQeNdNRFzPTuD9o4134n8bzacD9DV/WjcrXfRjbBCzzuUGj1e78+A7BWN7/5IWLz87KWk8G7O/W4+8PtEzlwkru6Wd1xO19GYU18oArCWCNoegSmcGn7w7NDEXlwD403oY8Oa7ylnbqGE28PVJx+HLPNIdSC6YKXeIOMnVs7Mctd/wXC93zGxAWD6ooTCzHSPVV50zKJmWIG2cVVUS7j35H3rGDtUHLI+ASXMEux9REZB8CdVOZMzp2wYeiOpggebJy6MKOZqPT1R3X0fqF2dHtRFPXrNsVr1Qt6bS9qTyO4ag1/BCvXF3P1uJEsI812BFAne3cYHy5bIOxuozPfipJrTb5WH35bxhElqwT3y/o/6JWOGg3HLDun31YmiZ2HScAsUAcEkA4hhoTNnqy4O2s3yVbCcR7jF7NLsbQc0MDTbnjxTdI4VnqUIn8s2c9hIJy/j80pmO9Bjxp+LQ9a2hUkfHgFhgHxZUVaeGVth8zG2kkgGdrp5VHhxMVFfvB26Ka6q6qE/UcS2lONSv+4T8niVRJz57qwctj8MNOkA3PTEfe/DP/LKMefke31YfT0xogHsLhDkx+mS8FCc01HReTjKLktk/Jh9mXwC5oKwueWWwlxI935ecn+3I2kAuOfMsgPLkoEBlwgiREC1pM7VVX1x8WmzIQVQTHd4iwnX96QewYckGRfNYWz/zwvWnjWlfcg8kRSe+68EHOGeRtC5r27fWLqRc0HNcjwpgHkI/b6czerCe8+07TWql4keJxJxhBYj3iOH7r9ZS8ck51XnOb8tGL1isimAJXodYGzakwktqHAD7MZhS+P02O+6jrg7d+yPC2ZCuS/3TOplYOCHQIhnZtR87PXTUwr83zfOwAwCyv6KP84JUQ45+DItrXLap7nOVZKQ5QxYIlbThAO6eima6Zu5XHfqGPMNWv0bLf5+vAjIa5np5DJrSwz9no/hj6CUh0iyI+SJq4RGI60lKtypMvF6MR3nHLEHOycRUQbZIyTHWl4QQLdHzuwN9lv10ouTEvNr6sFflAX2yb6w3hlCo7oBytH3rJekjb3IIOzBpeTPIejxzVlh0N9OT5MZdh4sNKYHUoWJ8mnfjdM+L4j5Q2Kgk/XiGDgEebkUxiEOQUdVpePF5uSCE+TPav/9FIRGXGiFn6NJMaU7aBsDTFBLloffFLYDpd8/bTwoSvifkj7buwLYM+h/qcnfdy5FWau1cKav+Blq/ZC0qBpo658RTC8ZtseAFDgXoQZuksM10hpP9bzD04Bx30xTGX81QbaSTNwSEEVrOtIhbDrj9OI43KH4O6zLzK+t30QxAv5zjk10RZ4+5SAdYndIlld9Y62opCfPDzRy3ubdve4ZEchpIKWTQvIxq3T5ogOhGaWBVYnkMtM2GVqvWV//46gET5SH/MdcwhACUcZ9kCpMnWH9CyyUwYvTT3UlNyV+DlS27LMPvaw7tx7qa+GfNCoCBd8S4esZpQYK/WReiS8=|pc7qpD42wxyXemdNPuwxbh8iIaryrBPu8f/DGwYdHTw="; + + let kdf = Kdf::PBKDF2 { + iterations: 100_000.try_into().unwrap(), + }; + + initialize_user_crypto( + &mut client, + InitUserCryptoRequest { + kdf_params: kdf.clone(), + email: "test@bitwarden.com".into(), + private_key: priv_key.to_owned(), + method: InitUserCryptoMethod::Password { + password: "asdfasdfasdf".into(), + user_key: "2.u2HDQ/nH2J7f5tYHctZx6Q==|NnUKODz8TPycWJA5svexe1wJIz2VexvLbZh2RDfhj5VI3wP8ZkR0Vicvdv7oJRyLI1GyaZDBCf9CTBunRTYUk39DbZl42Rb+Xmzds02EQhc=|rwuo5wgqvTJf3rgwOUfabUyzqhguMYb3sGBjOYqjevc=".into(), + }, + }, + ) + .await + .unwrap(); + + let new_password_response = update_password(&mut client, "123412341234".into()).unwrap(); + + let mut client2 = Client::new(None); + + initialize_user_crypto( + &mut client2, + InitUserCryptoRequest { + kdf_params: kdf.clone(), + email: "test@bitwarden.com".into(), + private_key: priv_key.to_owned(), + method: InitUserCryptoMethod::Password { + password: "123412341234".into(), + user_key: new_password_response.new_key.to_string(), + }, + }, + ) + .await + .unwrap(); + + let new_hash = client2 + .kdf() + .hash_password( + "test@bitwarden.com".into(), + "123412341234".into(), + kdf.clone(), + bitwarden_crypto::HashPurpose::ServerAuthorization, + ) + .await + .unwrap(); + + assert_eq!(new_hash, new_password_response.password_hash); + + assert_eq!( + client + .get_encryption_settings() + .unwrap() + .get_key(&None) + .unwrap() + .to_base64(), + client2 + .get_encryption_settings() + .unwrap() + .get_key(&None) + .unwrap() + .to_base64() + ); + } + #[tokio::test] async fn test_initialize_user_crypto_pin() { let mut client = Client::new(None); diff --git a/languages/kotlin/doc.md b/languages/kotlin/doc.md index fd98e463e..f85222738 100644 --- a/languages/kotlin/doc.md +++ b/languages/kotlin/doc.md @@ -311,6 +311,18 @@ as it can be used to decrypt all of the user's data **Output**: std::result::Result +### `update_password` + +Update the user's password, which will re-encrypt the user's encryption key with the new +password. This returns the new encrypted user key and the new password hash. + +**Arguments**: + +- self: +- new_password: String + +**Output**: std::result::Result + ### `derive_pin_key` Generates a PIN protected user key from the provided PIN. The result can be stored and later used to From 8b468ec34a770f8857408382b8a71624a9176ad1 Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Wed, 31 Jan 2024 14:20:04 +0100 Subject: [PATCH 13/34] Fix device keys incorrectly being treated as strings (#564) Device keys are generated from random u8 vectors. We incorrectly attempted to parse these as strings when decrypting them in the last trust device step. Added a complete test which confirms the whole device approval process. --- Cargo.lock | 1 + crates/bitwarden/Cargo.toml | 1 + crates/bitwarden/src/auth/auth_request.rs | 86 ++++++++++++++++++++--- 3 files changed, 80 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c4e1fe3c2..8ebf52136 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -373,6 +373,7 @@ dependencies = [ "uniffi", "uuid", "wiremock", + "zeroize", "zxcvbn", ] diff --git a/crates/bitwarden/Cargo.toml b/crates/bitwarden/Cargo.toml index 1f666e21c..b52e2f8f3 100644 --- a/crates/bitwarden/Cargo.toml +++ b/crates/bitwarden/Cargo.toml @@ -78,3 +78,4 @@ reqwest = { version = "*", features = [ rand_chacha = "0.3.1" tokio = { version = "1.35.1", features = ["rt", "macros"] } wiremock = "0.5.22" +zeroize = { version = ">=1.7.0, <2.0", features = ["derive", "aarch64"] } diff --git a/crates/bitwarden/src/auth/auth_request.rs b/crates/bitwarden/src/auth/auth_request.rs index facdc8f82..98fe4996a 100644 --- a/crates/bitwarden/src/auth/auth_request.rs +++ b/crates/bitwarden/src/auth/auth_request.rs @@ -58,9 +58,9 @@ pub(crate) fn auth_request_decrypt_user_key( user_key: AsymmetricEncString, ) -> Result { let key = AsymmetricCryptoKey::from_der(&STANDARD.decode(private_key)?)?; - let key: String = user_key.decrypt_with_key(&key)?; + let mut key: Vec = user_key.decrypt_with_key(&key)?; - Ok(key.parse()?) + Ok(SymmetricCryptoKey::try_from(key.as_mut_slice())?) } /// Approve an auth request. @@ -83,20 +83,25 @@ pub(crate) fn approve_auth_request( #[test] fn test_auth_request() { + use zeroize::Zeroizing; + let request = new_auth_request("test@bitwarden.com").unwrap(); - let secret = - "w2LO+nwV4oxwswVYCxlOfRUseXfvU03VzvKQHrqeklPgiMZrspUe6sOBToCnDn9Ay0tuCBn8ykVVRb7PWhub2Q=="; + let secret: &[u8] = &[ + 111, 32, 97, 169, 4, 241, 174, 74, 239, 206, 113, 86, 174, 68, 216, 238, 52, 85, 156, 27, + 134, 149, 54, 55, 91, 147, 45, 130, 131, 237, 51, 31, 191, 106, 155, 14, 160, 82, 47, 40, + 96, 31, 114, 127, 212, 187, 167, 110, 205, 116, 198, 243, 218, 72, 137, 53, 248, 43, 255, + 67, 35, 61, 245, 93, + ]; let private_key = AsymmetricCryptoKey::from_der(&STANDARD.decode(&request.private_key).unwrap()).unwrap(); - let encrypted = - AsymmetricEncString::encrypt_rsa2048_oaep_sha1(secret.as_bytes(), &private_key).unwrap(); + let encrypted = AsymmetricEncString::encrypt_rsa2048_oaep_sha1(secret, &private_key).unwrap(); let decrypted = auth_request_decrypt_user_key(request.private_key, encrypted).unwrap(); - assert_eq!(decrypted.to_base64(), secret); + assert_eq!(decrypted.to_vec(), Zeroizing::new(secret.to_owned())); } #[cfg(test)] @@ -106,7 +111,10 @@ mod tests { use bitwarden_crypto::Kdf; use super::*; - use crate::client::{LoginMethod, UserLoginMethod}; + use crate::{ + client::{LoginMethod, UserLoginMethod}, + mobile::crypto::{InitUserCryptoMethod, InitUserCryptoRequest}, + }; #[test] fn test_approve() { @@ -134,4 +142,66 @@ mod tests { approve_auth_request(&mut client, public_key.to_owned()).unwrap(); } + + #[tokio::test] + async fn test_device_login() { + let kdf = Kdf::PBKDF2 { + iterations: NonZeroU32::new(600_000).unwrap(), + }; + let email = "test@bitwarden.com"; + + let user_key = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=".parse().unwrap(); + let private_key = "2.yN7l00BOlUE0Sb0M//Q53w==|EwKG/BduQRQ33Izqc/ogoBROIoI5dmgrxSo82sgzgAMIBt3A2FZ9vPRMY+GWT85JiqytDitGR3TqwnFUBhKUpRRAq4x7rA6A1arHrFp5Tp1p21O3SfjtvB3quiOKbqWk6ZaU1Np9HwqwAecddFcB0YyBEiRX3VwF2pgpAdiPbSMuvo2qIgyob0CUoC/h4Bz1be7Qa7B0Xw9/fMKkB1LpOm925lzqosyMQM62YpMGkjMsbZz0uPopu32fxzDWSPr+kekNNyLt9InGhTpxLmq1go/pXR2uw5dfpXc5yuta7DB0EGBwnQ8Vl5HPdDooqOTD9I1jE0mRyuBpWTTI3FRnu3JUh3rIyGBJhUmHqGZvw2CKdqHCIrQeQkkEYqOeJRJVdBjhv5KGJifqT3BFRwX/YFJIChAQpebNQKXe/0kPivWokHWwXlDB7S7mBZzhaAPidZvnuIhalE2qmTypDwHy22FyqV58T8MGGMchcASDi/QXI6kcdpJzPXSeU9o+NC68QDlOIrMVxKFeE7w7PvVmAaxEo0YwmuAzzKy9QpdlK0aab/xEi8V4iXj4hGepqAvHkXIQd+r3FNeiLfllkb61p6WTjr5urcmDQMR94/wYoilpG5OlybHdbhsYHvIzYoLrC7fzl630gcO6t4nM24vdB6Ymg9BVpEgKRAxSbE62Tqacxqnz9AcmgItb48NiR/He3n3ydGjPYuKk/ihZMgEwAEZvSlNxYONSbYrIGDtOY+8Nbt6KiH3l06wjZW8tcmFeVlWv+tWotnTY9IqlAfvNVTjtsobqtQnvsiDjdEVtNy/s2ci5TH+NdZluca2OVEr91Wayxh70kpM6ib4UGbfdmGgCo74gtKvKSJU0rTHakQ5L9JlaSDD5FamBRyI0qfL43Ad9qOUZ8DaffDCyuaVyuqk7cz9HwmEmvWU3VQ+5t06n/5kRDXttcw8w+3qClEEdGo1KeENcnXCB32dQe3tDTFpuAIMLqwXs6FhpawfZ5kPYvLPczGWaqftIs/RXJ/EltGc0ugw2dmTLpoQhCqrcKEBDoYVk0LDZKsnzitOGdi9mOWse7Se8798ib1UsHFUjGzISEt6upestxOeupSTOh0v4+AjXbDzRUyogHww3V+Bqg71bkcMxtB+WM+pn1XNbVTyl9NR040nhP7KEf6e9ruXAtmrBC2ah5cFEpLIot77VFZ9ilLuitSz+7T8n1yAh1IEG6xxXxninAZIzi2qGbH69O5RSpOJuJTv17zTLJQIIc781JwQ2TTwTGnx5wZLbffhCasowJKd2EVcyMJyhz6ru0PvXWJ4hUdkARJs3Xu8dus9a86N8Xk6aAPzBDqzYb1vyFIfBxP0oO8xFHgd30Cgmz8UrSE3qeWRrF8ftrI6xQnFjHBGWD/JWSvd6YMcQED0aVuQkuNW9ST/DzQThPzRfPUoiL10yAmV7Ytu4fR3x2sF0Yfi87YhHFuCMpV/DsqxmUizyiJuD938eRcH8hzR/VO53Qo3UIsqOLcyXtTv6THjSlTopQ+JOLOnHm1w8dzYbLN44OG44rRsbihMUQp+wUZ6bsI8rrOnm9WErzkbQFbrfAINdoCiNa6cimYIjvvnMTaFWNymqY1vZxGztQiMiHiHYwTfwHTXrb9j0uPM=|09J28iXv9oWzYtzK2LBT6Yht4IT4MijEkk0fwFdrVQ4="; + + // Initialize an existing client which is unlocked + let mut existing_device = Client::new(None); + existing_device.set_login_method(LoginMethod::User(UserLoginMethod::Username { + client_id: "123".to_owned(), + email: email.to_owned(), + kdf: kdf.clone(), + })); + + existing_device + .initialize_user_crypto("asdfasdfasdf", user_key, private_key.parse().unwrap()) + .unwrap(); + + // Initialize a new device which will request to be logged in + let mut new_device = Client::new(None); + + // Initialize an auth request, and approve it on the existing device + let auth_req = new_auth_request(email).unwrap(); + let approved_req = approve_auth_request(&mut existing_device, auth_req.public_key).unwrap(); + + // Unlock the vault using the approved request + new_device + .crypto() + .initialize_user_crypto(InitUserCryptoRequest { + kdf_params: kdf, + email: email.to_owned(), + private_key: private_key.to_owned(), + method: InitUserCryptoMethod::AuthRequest { + request_private_key: auth_req.private_key, + protected_user_key: approved_req, + }, + }) + .await + .unwrap(); + + // We can validate that the vault is unlocked correctly by confirming the user key is the + // same + assert_eq!( + existing_device + .get_encryption_settings() + .unwrap() + .get_key(&None) + .unwrap() + .to_base64(), + new_device + .get_encryption_settings() + .unwrap() + .get_key(&None) + .unwrap() + .to_base64() + ); + } } From 4a2089cd9b885f51025d255f6afcc78ebf03b13e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ch=C4=99ci=C5=84ski?= Date: Wed, 31 Jan 2024 16:31:30 +0100 Subject: [PATCH 14/34] Add workflow linter (#567) ## Type of change ``` - [ ] Bug fix - [ ] New feature development - [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc) - [x] Build/deploy pipeline (DevOps) - [ ] Other ``` ## Objective ## Code changes - **.github/workflows/workflow-linter.yml:** Add workflow linter ## Before you submit - Please add **unit tests** where it makes sense to do so --- .github/workflows/workflow-linter.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/workflows/workflow-linter.yml diff --git a/.github/workflows/workflow-linter.yml b/.github/workflows/workflow-linter.yml new file mode 100644 index 000000000..24f10f1e4 --- /dev/null +++ b/.github/workflows/workflow-linter.yml @@ -0,0 +1,12 @@ +--- +name: Workflow linter + +on: + pull_request: + paths: + - .github/workflows/** + +jobs: + call-workflow: + name: Lint + uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@main From 8c6b21562bc5bb5b862a0116f74d375265382ea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ch=C4=99ci=C5=84ski?= Date: Wed, 31 Jan 2024 16:33:18 +0100 Subject: [PATCH 15/34] [DEVOPS-1655] Build publish workflow for python sdk (#558) ## Type of change ``` - [ ] Bug fix - [ ] New feature development - [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc) - [x] Build/deploy pipeline (DevOps) - [ ] Other ``` ## Objective Create publish workflow for Python SDK to publish it to pypi. ## Code changes - **.github/workflows/publish-python.yml:** Add steps to publish python SDK to pypi ## Before you submit - Please add **unit tests** where it makes sense to do so --------- Co-authored-by: MtnBurrit0 <77340197+mimartin12@users.noreply.github.com> --- .github/workflows/publish-python.yml | 94 ++++++++++++++++++++++++++-- .github/workflows/version-bump.yml | 8 +++ 2 files changed, 98 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish-python.yml b/.github/workflows/publish-python.yml index bccde1aa2..780195161 100644 --- a/.github/workflows/publish-python.yml +++ b/.github/workflows/publish-python.yml @@ -1,13 +1,99 @@ --- name: Publish Python SDK +run-name: Publish Python SDK ${{ inputs.release_type }} on: workflow_dispatch: + inputs: + release_type: + description: "Release Options" + required: true + default: "Release" + type: choice + options: + - Release + - Dry Run + +defaults: + run: + shell: bash jobs: - stub: - name: Stub + setup: + name: Setup runs-on: ubuntu-22.04 steps: - - name: Stub - run: echo "Stub" + - name: Checkout repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: Branch check + if: ${{ github.event.inputs.release_type != 'Dry Run' }} + run: | + if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc" ]]; then + echo "===================================" + echo "[!] Can only release from the 'rc' or 'hotfix-rc' branches" + echo "===================================" + exit 1 + fi + + publish: + name: Publish + runs-on: ubuntu-22.04 + needs: setup + steps: + - name: Install Python + uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0 + with: + python-version: "3.9" + + - name: Install twine + run: pip install twine + + - name: Download artifacts + uses: dawidd6/action-download-artifact@e7466d1a7587ed14867642c2ca74b5bcc1e19a2d # v3.0.0 + with: + workflow: build-python-wheels.yml + path: ${{ github.workspace }}/target/wheels/dist + workflow_conclusion: success + branch: ${{ github.event.inputs.release_type == 'Dry Run' && 'main' || github.ref_name }} + name: bitwarden_sdk(.*) + name_is_regexp: true + + - name: Move files + working-directory: ${{ github.workspace }}/target/wheels/dist + run: | + find . -maxdepth 2 -type f -print0 | xargs -0 mv -t . + rm -rf */ + + - name: Login to Azure + uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0 + with: + creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} + + - name: Retrieve pypi api token + id: retrieve-secret + uses: bitwarden/gh-actions/get-keyvault-secrets@main + with: + keyvault: "bitwarden-ci" + secrets: "pypi-api-token, + pypi-test-api-token" + + - name: Check + working-directory: ${{ github.workspace }}/target/wheels + run: twine check dist/* + + - name: Publish + if: ${{ github.event.inputs.release_type != 'Dry Run' }} + working-directory: ${{ github.workspace }}/target/wheels + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ steps.retrieve-secret.outputs.pypi-api-token }} + run: twine upload --repository pypi dist/* + + - name: Dry Run - Publish + if: ${{ github.event.inputs.release_type == 'Dry Run' }} + working-directory: ${{ github.workspace }}/target/wheels + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ steps.retrieve-secret.outputs.pypi-test-api-token }} + run: twine upload --repository testpypi dist/* diff --git a/.github/workflows/version-bump.yml b/.github/workflows/version-bump.yml index c3055f097..e40f68a8e 100644 --- a/.github/workflows/version-bump.yml +++ b/.github/workflows/version-bump.yml @@ -18,6 +18,7 @@ on: - bitwarden-json - cli - napi + - python-sdk version_number: description: "New version (example: '2024.1.0')" required: true @@ -142,6 +143,13 @@ jobs: if: ${{ inputs.project == 'bitwarden-json' }} run: cargo-set-version set-version -p bitwarden-json ${{ inputs.version_number }} + ### python + - name: Bump python-sdk Version + if: ${{ inputs.project == 'python-sdk' }} + run: | + sed -i 's/version = "[0-9]\.[0-9]\.[0-9]"/version = "${{ inputs.version_number }}"/' ./languages/python/pyproject.toml + sed -i 's/__version__ = "[0-9]\.[0-9]\.[0-9]"/__version__ = "${{ inputs.version_number }}"/' ./languages/python/bitwarden_sdk/__init__.py + ############################ # VERSION BUMP SECTION END # ############################ From 872a05b7ba7c9ce267b64dc91b2ec53c4e4e7068 Mon Sep 17 00:00:00 2001 From: tangowithfoxtrot <5676771+tangowithfoxtrot@users.noreply.github.com> Date: Wed, 31 Jan 2024 08:51:16 -0800 Subject: [PATCH 16/34] Update Python README (#563) ## Type of change Fix errors in README and update the Python package name in preparation for publishing to PyPI. - [ ] Bug fix - [ ] New feature development - [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc) - [ ] Build/deploy pipeline (DevOps) - [x] Other ## Before you submit - Please add **unit tests** where it makes sense to do so --- languages/python/README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/languages/python/README.md b/languages/python/README.md index e77e7a8eb..f41fd8a12 100644 --- a/languages/python/README.md +++ b/languages/python/README.md @@ -2,7 +2,7 @@ ## Requirements - Python 3 -- `maturin` (install with `pip install maturin[patchelf]`) +- `maturin` (install with `pip install maturin`) - `npm` ## Build @@ -15,12 +15,15 @@ cd languages/python/ maturin develop ``` -You can now import `BitwardenClient` in your Python code. +You can now import `BitwardenClient` in your Python code with: +```python +from bitwarden_sdk import BitwardenClient +``` # Use without building locally ```bash -pip install BitwardenClient +pip install bitwarden-sdk ``` # Run From 2ac95f17ec1793fc5319d3892e5ab4b0070bc173 Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Thu, 1 Feb 2024 16:31:13 +0100 Subject: [PATCH 17/34] Implement alternative login with device flow (#568) When approving from other devices we need to support the master key flow. Also implements login with device for the CLI as a testing tool. --- .../bitwarden-crypto/src/keys/master_key.rs | 4 + .../api/request/auth_request_token_request.rs | 59 ++++++++ crates/bitwarden/src/auth/api/request/mod.rs | 5 + .../api/request/password_token_request.rs | 14 +- crates/bitwarden/src/auth/auth_request.rs | 67 ++++++++- crates/bitwarden/src/auth/client_auth.rs | 21 +++ .../bitwarden/src/auth/login/auth_request.rs | 131 ++++++++++++++++++ crates/bitwarden/src/auth/login/mod.rs | 7 + crates/bitwarden/src/auth/login/password.rs | 16 ++- crates/bitwarden/src/auth/mod.rs | 4 +- crates/bitwarden/src/client/client.rs | 33 +++-- crates/bitwarden/src/mobile/crypto.rs | 35 ++++- crates/bw/src/auth/login.rs | 23 +++ crates/bw/src/auth/mod.rs | 2 +- crates/bw/src/main.rs | 11 ++ languages/kotlin/doc.md | 4 +- 16 files changed, 405 insertions(+), 31 deletions(-) create mode 100644 crates/bitwarden/src/auth/api/request/auth_request_token_request.rs create mode 100644 crates/bitwarden/src/auth/login/auth_request.rs diff --git a/crates/bitwarden-crypto/src/keys/master_key.rs b/crates/bitwarden-crypto/src/keys/master_key.rs index 920f103e4..0a435ed88 100644 --- a/crates/bitwarden-crypto/src/keys/master_key.rs +++ b/crates/bitwarden-crypto/src/keys/master_key.rs @@ -39,6 +39,10 @@ pub enum HashPurpose { pub struct MasterKey(SymmetricCryptoKey); impl MasterKey { + pub fn new(key: SymmetricCryptoKey) -> MasterKey { + Self(key) + } + /// Derives a users master key from their password, email and KDF. pub fn derive(password: &[u8], email: &[u8], kdf: &Kdf) -> Result { derive_key(password, email, kdf).map(Self) diff --git a/crates/bitwarden/src/auth/api/request/auth_request_token_request.rs b/crates/bitwarden/src/auth/api/request/auth_request_token_request.rs new file mode 100644 index 000000000..cf5ae7ee4 --- /dev/null +++ b/crates/bitwarden/src/auth/api/request/auth_request_token_request.rs @@ -0,0 +1,59 @@ +use log::debug; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +use crate::{ + auth::api::response::IdentityTokenResponse, + client::{client_settings::DeviceType, ApiConfigurations}, + error::Result, +}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct AuthRequestTokenRequest { + scope: String, + client_id: String, + #[serde(rename = "deviceType")] + device_type: u8, + #[serde(rename = "deviceIdentifier")] + device_identifier: String, + #[serde(rename = "deviceName")] + device_name: String, + grant_type: String, + #[serde(rename = "username")] + email: String, + #[serde(rename = "authRequest")] + auth_request_id: Uuid, + #[serde(rename = "password")] + access_code: String, +} + +impl AuthRequestTokenRequest { + pub fn new( + email: &str, + auth_request_id: &Uuid, + access_code: &str, + device_type: DeviceType, + device_identifier: &str, + ) -> Self { + let obj = Self { + scope: "api offline_access".to_string(), + client_id: "web".to_string(), + device_type: device_type as u8, + device_identifier: device_identifier.to_string(), + device_name: "chrome".to_string(), + grant_type: "password".to_string(), + email: email.to_string(), + auth_request_id: *auth_request_id, + access_code: access_code.to_string(), + }; + debug!("initializing {:?}", obj); + obj + } + + pub(crate) async fn send( + &self, + configurations: &ApiConfigurations, + ) -> Result { + super::send_identity_connect_request(configurations, Some(&self.email), &self).await + } +} diff --git a/crates/bitwarden/src/auth/api/request/mod.rs b/crates/bitwarden/src/auth/api/request/mod.rs index 67796f2f3..2b5bde225 100644 --- a/crates/bitwarden/src/auth/api/request/mod.rs +++ b/crates/bitwarden/src/auth/api/request/mod.rs @@ -15,6 +15,11 @@ pub(crate) use password_token_request::*; #[cfg(feature = "internal")] pub(crate) use renew_token_request::*; +#[cfg(feature = "mobile")] +mod auth_request_token_request; +#[cfg(feature = "mobile")] +pub(crate) use auth_request_token_request::*; + use crate::{ auth::api::response::{parse_identity_response, IdentityTokenResponse}, client::ApiConfigurations, diff --git a/crates/bitwarden/src/auth/api/request/password_token_request.rs b/crates/bitwarden/src/auth/api/request/password_token_request.rs index fd016d898..2f6414bcd 100644 --- a/crates/bitwarden/src/auth/api/request/password_token_request.rs +++ b/crates/bitwarden/src/auth/api/request/password_token_request.rs @@ -6,7 +6,7 @@ use crate::{ api::response::IdentityTokenResponse, login::{TwoFactorProvider, TwoFactorRequest}, }, - client::ApiConfigurations, + client::{client_settings::DeviceType, ApiConfigurations}, error::Result, }; @@ -35,13 +35,19 @@ pub struct PasswordTokenRequest { } impl PasswordTokenRequest { - pub fn new(email: &str, password_hash: &String, two_factor: &Option) -> Self { + pub fn new( + email: &str, + password_hash: &str, + device_type: DeviceType, + device_identifier: &str, + two_factor: &Option, + ) -> Self { let tf = two_factor.as_ref(); let obj = Self { scope: "api offline_access".to_string(), client_id: "web".to_string(), - device_type: 10, - device_identifier: "b86dd6ab-4265-4ddf-a7f1-eb28d5677f33".to_string(), + device_type: device_type as u8, + device_identifier: device_identifier.to_string(), device_name: "firefox".to_string(), grant_type: "password".to_string(), master_password_hash: password_hash.to_string(), diff --git a/crates/bitwarden/src/auth/auth_request.rs b/crates/bitwarden/src/auth/auth_request.rs index 98fe4996a..18c71afba 100644 --- a/crates/bitwarden/src/auth/auth_request.rs +++ b/crates/bitwarden/src/auth/auth_request.rs @@ -3,7 +3,7 @@ use bitwarden_crypto::{ fingerprint, AsymmetricCryptoKey, AsymmetricEncString, AsymmetricPublicCryptoKey, }; #[cfg(feature = "mobile")] -use bitwarden_crypto::{KeyDecryptable, SymmetricCryptoKey}; +use bitwarden_crypto::{EncString, KeyDecryptable, SymmetricCryptoKey}; use bitwarden_generators::{password, PasswordGeneratorRequest}; use crate::{error::Error, Client}; @@ -63,6 +63,22 @@ pub(crate) fn auth_request_decrypt_user_key( Ok(SymmetricCryptoKey::try_from(key.as_mut_slice())?) } +/// Decrypt the user key using the private key generated previously. +#[cfg(feature = "mobile")] +pub(crate) fn auth_request_decrypt_master_key( + private_key: String, + master_key: AsymmetricEncString, + user_key: EncString, +) -> Result { + use bitwarden_crypto::MasterKey; + + let key = AsymmetricCryptoKey::from_der(&STANDARD.decode(private_key)?)?; + let mut master_key: Vec = master_key.decrypt_with_key(&key)?; + let master_key = MasterKey::new(SymmetricCryptoKey::try_from(master_key.as_mut_slice())?); + + Ok(master_key.decrypt_user_key(user_key)?) +} + /// Approve an auth request. /// /// Encrypts the user key with a public key. @@ -113,14 +129,14 @@ mod tests { use super::*; use crate::{ client::{LoginMethod, UserLoginMethod}, - mobile::crypto::{InitUserCryptoMethod, InitUserCryptoRequest}, + mobile::crypto::{AuthRequestMethod, InitUserCryptoMethod, InitUserCryptoRequest}, }; #[test] fn test_approve() { let mut client = Client::new(None); client.set_login_method(LoginMethod::User(UserLoginMethod::Username { - client_id: "123".to_owned(), + client_id: "7b821276-e27c-400b-9853-606393c87f18".to_owned(), email: "test@bitwarden.com".to_owned(), kdf: Kdf::PBKDF2 { iterations: NonZeroU32::new(600_000).unwrap(), @@ -133,16 +149,53 @@ mod tests { .initialize_user_crypto("asdfasdfasdf", user_key, private_key) .unwrap(); - let public_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnRtpYLp9QLaEUkdPkWZX6TrMUKFoSaFamBKDL0NlS6xwtETTqYIxRVsvnHii3Dhz+fh3aHQVyBa1rBXogeH3MLERzNADwZhpWtBT9wKCXY5o0fIWYdZV/Nf0Y+0ZoKdImrGPLPmyHGfCqrvrK7g09q8+3kXUlkdAImlQqc5TiYwiHBfUQVTBq/Ae7a0FEpajx1NUM4h3edpCYxbvnpSTuzMgbmbUUS4gdCaheA2ibYxy/zkLzsaLygoibMyGNl9Y8J5n7dDrVXpUKZTihVfXwHfEZwtKNunWsmmt8rEJWVpguUDEDVSUogoxQcNaCi7KHn9ioSip76hg1jLpypO3WwIDAQAB"; + let public_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvyLRDUwXB4BfQ507D4meFPmwn5zwy3IqTPJO4plrrhnclWahXa240BzyFW9gHgYu+Jrgms5xBfRTBMcEsqqNm7+JpB6C1B6yvnik0DpJgWQw1rwvy4SUYidpR/AWbQi47n/hvnmzI/sQxGddVfvWu1iTKOlf5blbKYAXnUE5DZBGnrWfacNXwRRdtP06tFB0LwDgw+91CeLSJ9py6dm1qX5JIxoO8StJOQl65goLCdrTWlox+0Jh4xFUfCkb+s3px+OhSCzJbvG/hlrSRcUz5GnwlCEyF3v5lfUtV96MJD+78d8pmH6CfFAp2wxKRAbGdk+JccJYO6y6oIXd3Fm7twIDAQAB"; // Verify fingerprint let pbkey = STANDARD.decode(public_key).unwrap(); let fingerprint = fingerprint("test@bitwarden.com", &pbkey).unwrap(); - assert_eq!(fingerprint, "spill-applaud-sweep-habitable-shrunk"); + assert_eq!(fingerprint, "childless-unfair-prowler-dropbox-designate"); approve_auth_request(&mut client, public_key.to_owned()).unwrap(); } + #[tokio::test] + async fn test_decrypt_user_key() { + let private_key = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCzLtEUdxfcLxDj84yaGFsVF5hZ8Hjlb08NMQDy1RnBma06I3ZESshLYzVz4r/gegMn9OOltfV/Yxlyvida8oW6qdlfJ7AVz6Oa8pV7BiL40C7b76+oqraQpyYw2HChANB1AhXL9SqWngKmLZwjA7qiCrmcc0kZHeOb4KnKtp9iVvPVs+8veFvKgYO4ba2AAOHKFdR0W55/agXfAy+fWUAkC8mc9ikyJdQWaPV6OZvC2XFkOseBQm9Rynudh3BQpoWiL6w620efe7t5k+02/EyOFJL9f/XEEjM/+Yo0t3LAfkuhHGeKiRST59Xc9hTEmyJTeVXROtz+0fjqOp3xkaObAgMBAAECggEACs4xhnO0HaZhh1/iH7zORMIRXKeyxP2LQiTR8xwN5JJ9wRWmGAR9VasS7EZFTDidIGVME2u/h4s5EqXnhxfO+0gGksVvgNXJ/qw87E8K2216g6ZNo6vSGA7H1GH2voWwejJ4/k/cJug6dz2S402rRAKh2Wong1arYHSkVlQp3diiMa5FHAOSE+Cy09O2ZsaF9IXQYUtlW6AVXFrBEPYH2kvkaPXchh8VETMijo6tbvoKLnUHe+wTaDMls7hy8exjtVyI59r3DNzjy1lNGaGb5QSnFMXR+eHhPZc844Wv02MxC15zKABADrl58gpJyjTl6XpDdHCYGsmGpVGH3X9TQQKBgQDz/9beFjzq59ve6rGwn+EtnQfSsyYT+jr7GN8lNEXb3YOFXBgPhfFIcHRh2R00Vm9w2ApfAx2cd8xm2I6HuvQ1Os7g26LWazvuWY0Qzb+KaCLQTEGH1RnTq6CCG+BTRq/a3J8M4t38GV5TWlzv8wr9U4dl6FR4efjb65HXs1GQ4QKBgQC7/uHfrOTEHrLeIeqEuSl0vWNqEotFKdKLV6xpOvNuxDGbgW4/r/zaxDqt0YBOXmRbQYSEhmO3oy9J6XfE1SUln0gbavZeW0HESCAmUIC88bDnspUwS9RxauqT5aF8ODKN/bNCWCnBM1xyonPOs1oT1nyparJVdQoG//Y7vkB3+wKBgBqLqPq8fKAp3XfhHLfUjREDVoiLyQa/YI9U42IOz9LdxKNLo6p8rgVthpvmnRDGnpUuS+KOWjhdqDVANjF6G3t3DG7WNl8Rh5Gk2H4NhFswfSkgQrjebFLlBy9gjQVCWXt8KSmjvPbiY6q52Aaa8IUjA0YJAregvXxfopxO+/7BAoGARicvEtDp7WWnSc1OPoj6N14VIxgYcI7SyrzE0d/1x3ffKzB5e7qomNpxKzvqrVP8DzG7ydh8jaKPmv1MfF8tpYRy3AhmN3/GYwCnPqT75YYrhcrWcVdax5gmQVqHkFtIQkRSCIftzPLlpMGKha/YBV8c1fvC4LD0NPh/Ynv0gtECgYEAyOZg95/kte0jpgUEgwuMrzkhY/AaUJULFuR5MkyvReEbtSBQwV5tx60+T95PHNiFooWWVXiLMsAgyI2IbkxVR1Pzdri3gWK5CTfqb7kLuaj/B7SGvBa2Sxo478KS5K8tBBBWkITqo+wLC0mn3uZi1dyMWO1zopTA+KtEGF2dtGQ="; + + let enc_user_key = "4.dxbd5OMwi/Avy7DQxvLV+Z7kDJgHBtg/jAbgYNO7QU0Zii4rLFNco2lS5aS9z42LTZHc2p5HYwn2ZwkZNfHsQ6//d5q40MDgGYJMKBXOZP62ZHhct1XsvYBmtcUtIOm5j2HSjt2pjEuGAc1LbyGIWRJJQ3Lp1ULbL2m71I+P23GF36JyOM8SUWvpvxE/3+qqVhRFPG2VqMCYa2kLLxwVfUmpV+KKjX1TXsrq6pfJIwHNwHw4h7MSfD8xTy2bx4MiBt638Z9Vt1pGsSQkh9RgPvCbnhuCpZQloUgJ8ByLVEcrlKx3yaaxiQXvte+ZhuOI7rGdjmoVoOzisooje4JgYw==".parse().unwrap(); + let dec = auth_request_decrypt_user_key(private_key.to_owned(), enc_user_key).unwrap(); + + assert_eq!( + dec.to_vec().as_ref(), + vec![ + 201, 37, 234, 213, 21, 75, 40, 70, 149, 213, 234, 16, 19, 251, 162, 245, 161, 74, + 34, 245, 211, 151, 211, 192, 95, 10, 117, 50, 88, 223, 23, 157 + ] + ); + } + + #[tokio::test] + async fn test_decrypt_master_key() { + let private_key = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCzLtEUdxfcLxDj84yaGFsVF5hZ8Hjlb08NMQDy1RnBma06I3ZESshLYzVz4r/gegMn9OOltfV/Yxlyvida8oW6qdlfJ7AVz6Oa8pV7BiL40C7b76+oqraQpyYw2HChANB1AhXL9SqWngKmLZwjA7qiCrmcc0kZHeOb4KnKtp9iVvPVs+8veFvKgYO4ba2AAOHKFdR0W55/agXfAy+fWUAkC8mc9ikyJdQWaPV6OZvC2XFkOseBQm9Rynudh3BQpoWiL6w620efe7t5k+02/EyOFJL9f/XEEjM/+Yo0t3LAfkuhHGeKiRST59Xc9hTEmyJTeVXROtz+0fjqOp3xkaObAgMBAAECggEACs4xhnO0HaZhh1/iH7zORMIRXKeyxP2LQiTR8xwN5JJ9wRWmGAR9VasS7EZFTDidIGVME2u/h4s5EqXnhxfO+0gGksVvgNXJ/qw87E8K2216g6ZNo6vSGA7H1GH2voWwejJ4/k/cJug6dz2S402rRAKh2Wong1arYHSkVlQp3diiMa5FHAOSE+Cy09O2ZsaF9IXQYUtlW6AVXFrBEPYH2kvkaPXchh8VETMijo6tbvoKLnUHe+wTaDMls7hy8exjtVyI59r3DNzjy1lNGaGb5QSnFMXR+eHhPZc844Wv02MxC15zKABADrl58gpJyjTl6XpDdHCYGsmGpVGH3X9TQQKBgQDz/9beFjzq59ve6rGwn+EtnQfSsyYT+jr7GN8lNEXb3YOFXBgPhfFIcHRh2R00Vm9w2ApfAx2cd8xm2I6HuvQ1Os7g26LWazvuWY0Qzb+KaCLQTEGH1RnTq6CCG+BTRq/a3J8M4t38GV5TWlzv8wr9U4dl6FR4efjb65HXs1GQ4QKBgQC7/uHfrOTEHrLeIeqEuSl0vWNqEotFKdKLV6xpOvNuxDGbgW4/r/zaxDqt0YBOXmRbQYSEhmO3oy9J6XfE1SUln0gbavZeW0HESCAmUIC88bDnspUwS9RxauqT5aF8ODKN/bNCWCnBM1xyonPOs1oT1nyparJVdQoG//Y7vkB3+wKBgBqLqPq8fKAp3XfhHLfUjREDVoiLyQa/YI9U42IOz9LdxKNLo6p8rgVthpvmnRDGnpUuS+KOWjhdqDVANjF6G3t3DG7WNl8Rh5Gk2H4NhFswfSkgQrjebFLlBy9gjQVCWXt8KSmjvPbiY6q52Aaa8IUjA0YJAregvXxfopxO+/7BAoGARicvEtDp7WWnSc1OPoj6N14VIxgYcI7SyrzE0d/1x3ffKzB5e7qomNpxKzvqrVP8DzG7ydh8jaKPmv1MfF8tpYRy3AhmN3/GYwCnPqT75YYrhcrWcVdax5gmQVqHkFtIQkRSCIftzPLlpMGKha/YBV8c1fvC4LD0NPh/Ynv0gtECgYEAyOZg95/kte0jpgUEgwuMrzkhY/AaUJULFuR5MkyvReEbtSBQwV5tx60+T95PHNiFooWWVXiLMsAgyI2IbkxVR1Pzdri3gWK5CTfqb7kLuaj/B7SGvBa2Sxo478KS5K8tBBBWkITqo+wLC0mn3uZi1dyMWO1zopTA+KtEGF2dtGQ="; + + let enc_master_key = "4.dxbd5OMwi/Avy7DQxvLV+Z7kDJgHBtg/jAbgYNO7QU0Zii4rLFNco2lS5aS9z42LTZHc2p5HYwn2ZwkZNfHsQ6//d5q40MDgGYJMKBXOZP62ZHhct1XsvYBmtcUtIOm5j2HSjt2pjEuGAc1LbyGIWRJJQ3Lp1ULbL2m71I+P23GF36JyOM8SUWvpvxE/3+qqVhRFPG2VqMCYa2kLLxwVfUmpV+KKjX1TXsrq6pfJIwHNwHw4h7MSfD8xTy2bx4MiBt638Z9Vt1pGsSQkh9RgPvCbnhuCpZQloUgJ8ByLVEcrlKx3yaaxiQXvte+ZhuOI7rGdjmoVoOzisooje4JgYw==".parse().unwrap(); + let enc_user_key = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=".parse().unwrap(); + let dec = + auth_request_decrypt_master_key(private_key.to_owned(), enc_master_key, enc_user_key) + .unwrap(); + + assert_eq!( + dec.to_vec().as_ref(), + vec![ + 109, 128, 172, 147, 206, 123, 134, 95, 16, 36, 155, 113, 201, 18, 186, 230, 216, + 212, 173, 188, 74, 11, 134, 131, 137, 242, 105, 178, 105, 126, 52, 139, 248, 91, + 215, 21, 128, 91, 226, 222, 165, 67, 251, 34, 83, 81, 77, 147, 225, 76, 13, 41, + 102, 45, 183, 218, 106, 89, 254, 208, 251, 101, 130, 10, + ] + ); + } + #[tokio::test] async fn test_device_login() { let kdf = Kdf::PBKDF2 { @@ -181,7 +234,9 @@ mod tests { private_key: private_key.to_owned(), method: InitUserCryptoMethod::AuthRequest { request_private_key: auth_req.private_key, - protected_user_key: approved_req, + method: AuthRequestMethod::UserKey { + protected_user_key: approved_req, + }, }, }) .await diff --git a/crates/bitwarden/src/auth/client_auth.rs b/crates/bitwarden/src/auth/client_auth.rs index aaa387741..0ae2f6f33 100644 --- a/crates/bitwarden/src/auth/client_auth.rs +++ b/crates/bitwarden/src/auth/client_auth.rs @@ -1,6 +1,8 @@ #[cfg(feature = "internal")] use bitwarden_crypto::{AsymmetricEncString, DeviceKey, TrustDeviceResponse}; +#[cfg(feature = "mobile")] +use crate::auth::login::NewAuthRequestResponse; #[cfg(feature = "secrets")] use crate::auth::login::{login_access_token, AccessTokenLoginRequest, AccessTokenLoginResponse}; use crate::{auth::renew::renew_token, error::Result, Client}; @@ -116,6 +118,25 @@ impl<'a> ClientAuth<'a> { } } +#[cfg(feature = "mobile")] +impl<'a> ClientAuth<'a> { + pub async fn login_device( + &mut self, + email: String, + device_identifier: String, + ) -> Result { + use crate::auth::login::send_new_auth_request; + + send_new_auth_request(self.client, email, device_identifier).await + } + + pub async fn login_device_complete(&mut self, auth_req: NewAuthRequestResponse) -> Result<()> { + use crate::auth::login::complete_auth_request; + + complete_auth_request(self.client, auth_req).await + } +} + #[cfg(feature = "internal")] fn trust_device(client: &Client) -> Result { let enc = client.get_encryption_settings()?; diff --git a/crates/bitwarden/src/auth/login/auth_request.rs b/crates/bitwarden/src/auth/login/auth_request.rs new file mode 100644 index 000000000..30db06124 --- /dev/null +++ b/crates/bitwarden/src/auth/login/auth_request.rs @@ -0,0 +1,131 @@ +use std::num::NonZeroU32; + +use bitwarden_api_api::{ + apis::auth_requests_api::{auth_requests_id_response_get, auth_requests_post}, + models::{AuthRequestCreateRequestModel, AuthRequestType}, +}; +use bitwarden_crypto::Kdf; +use uuid::Uuid; + +use crate::{ + auth::{ + api::{request::AuthRequestTokenRequest, response::IdentityTokenResponse}, + auth_request::new_auth_request, + }, + client::{LoginMethod, UserLoginMethod}, + error::Result, + mobile::crypto::{AuthRequestMethod, InitUserCryptoMethod, InitUserCryptoRequest}, + Client, +}; + +pub struct NewAuthRequestResponse { + pub fingerprint: String, + email: String, + device_identifier: String, + auth_request_id: Uuid, + access_code: String, + private_key: String, +} + +pub(crate) async fn send_new_auth_request( + client: &mut Client, + email: String, + device_identifier: String, +) -> Result { + let config = client.get_api_configurations().await; + + let auth = new_auth_request(&email)?; + + let req = AuthRequestCreateRequestModel { + email: email.clone(), + public_key: auth.public_key, + device_identifier: device_identifier.clone(), + access_code: auth.access_code.clone(), + r#type: AuthRequestType::Variant0, // AuthenticateAndUnlock + }; + + let res = auth_requests_post(&config.api, Some(req)).await?; + + Ok(NewAuthRequestResponse { + fingerprint: auth.fingerprint, + email, + device_identifier, + auth_request_id: res.id.unwrap(), + access_code: auth.access_code, + private_key: auth.private_key, + }) +} + +pub(crate) async fn complete_auth_request( + client: &mut Client, + auth_req: NewAuthRequestResponse, +) -> Result<()> { + let config = client.get_api_configurations().await; + + let res = auth_requests_id_response_get( + &config.api, + auth_req.auth_request_id, + Some(&auth_req.access_code), + ) + .await?; + + let approved = res.request_approved.unwrap_or(false); + + if !approved { + return Err("Auth request was not approved".into()); + } + + let response = AuthRequestTokenRequest::new( + &auth_req.email, + &auth_req.auth_request_id, + &auth_req.access_code, + config.device_type, + &auth_req.device_identifier, + ) + .send(config) + .await?; + + if let IdentityTokenResponse::Authenticated(r) = response { + let kdf = Kdf::PBKDF2 { + iterations: NonZeroU32::new(600_000).unwrap(), + }; + + client.set_tokens( + r.access_token.clone(), + r.refresh_token.clone(), + r.expires_in, + ); + client.set_login_method(LoginMethod::User(UserLoginMethod::Username { + client_id: "web".to_owned(), + email: auth_req.email.to_owned(), + kdf: kdf.clone(), + })); + + let method = match res.master_password_hash { + Some(_) => AuthRequestMethod::MasterKey { + protected_master_key: res.key.unwrap().parse().unwrap(), + auth_request_key: r.key.unwrap().parse().unwrap(), + }, + None => AuthRequestMethod::UserKey { + protected_user_key: res.key.unwrap().parse().unwrap(), + }, + }; + + client + .crypto() + .initialize_user_crypto(InitUserCryptoRequest { + kdf_params: kdf, + email: auth_req.email, + private_key: r.private_key.unwrap(), + method: InitUserCryptoMethod::AuthRequest { + request_private_key: auth_req.private_key, + method, + }, + }) + .await?; + + Ok(()) + } else { + Err("Failed to authenticate".into()) + } +} diff --git a/crates/bitwarden/src/auth/login/mod.rs b/crates/bitwarden/src/auth/login/mod.rs index afd7873a4..a36d27ae9 100644 --- a/crates/bitwarden/src/auth/login/mod.rs +++ b/crates/bitwarden/src/auth/login/mod.rs @@ -29,6 +29,13 @@ pub(crate) use api_key::login_api_key; #[cfg(feature = "internal")] pub use api_key::{ApiKeyLoginRequest, ApiKeyLoginResponse}; +#[cfg(feature = "mobile")] +mod auth_request; +#[cfg(feature = "mobile")] +pub use auth_request::NewAuthRequestResponse; +#[cfg(feature = "mobile")] +pub(crate) use auth_request::{complete_auth_request, send_new_auth_request}; + #[cfg(feature = "secrets")] mod access_token; #[cfg(feature = "secrets")] diff --git a/crates/bitwarden/src/auth/login/password.rs b/crates/bitwarden/src/auth/login/password.rs index f873ace97..4c978e559 100644 --- a/crates/bitwarden/src/auth/login/password.rs +++ b/crates/bitwarden/src/auth/login/password.rs @@ -63,12 +63,20 @@ pub(crate) async fn login_password( async fn request_identity_tokens( client: &mut Client, input: &PasswordLoginRequest, - password_hash: &String, + password_hash: &str, ) -> Result { + use crate::client::client_settings::DeviceType; + let config = client.get_api_configurations().await; - PasswordTokenRequest::new(&input.email, password_hash, &input.two_factor) - .send(config) - .await + PasswordTokenRequest::new( + &input.email, + password_hash, + DeviceType::ChromeBrowser, + "b86dd6ab-4265-4ddf-a7f1-eb28d5677f33", + &input.two_factor, + ) + .send(config) + .await } #[cfg(feature = "internal")] diff --git a/crates/bitwarden/src/auth/mod.rs b/crates/bitwarden/src/auth/mod.rs index 23b64eaf9..d77847574 100644 --- a/crates/bitwarden/src/auth/mod.rs +++ b/crates/bitwarden/src/auth/mod.rs @@ -14,10 +14,10 @@ use bitwarden_crypto::{HashPurpose, MasterKey}; pub use register::{RegisterKeyResponse, RegisterRequest}; #[cfg(feature = "internal")] mod auth_request; -#[cfg(feature = "mobile")] -pub(crate) use auth_request::auth_request_decrypt_user_key; #[cfg(feature = "internal")] pub use auth_request::AuthRequestResponse; +#[cfg(feature = "mobile")] +pub(crate) use auth_request::{auth_request_decrypt_master_key, auth_request_decrypt_user_key}; #[cfg(feature = "internal")] use crate::{client::Kdf, error::Result}; diff --git a/crates/bitwarden/src/client/client.rs b/crates/bitwarden/src/client/client.rs index 3ccb7f9ca..b8632e21e 100644 --- a/crates/bitwarden/src/client/client.rs +++ b/crates/bitwarden/src/client/client.rs @@ -6,7 +6,7 @@ use bitwarden_crypto::SymmetricCryptoKey; #[cfg(feature = "internal")] use bitwarden_crypto::{AsymmetricEncString, EncString}; use chrono::Utc; -use reqwest::header::{self}; +use reqwest::header::{self, HeaderValue}; use uuid::Uuid; use super::AccessToken; @@ -29,6 +29,9 @@ use crate::{ pub(crate) struct ApiConfigurations { pub identity: bitwarden_api_identity::apis::configuration::Configuration, pub api: bitwarden_api_api::apis::configuration::Configuration, + /// Reqwest client useable for external integrations like email forwarders, HIBP. + #[allow(unused)] + pub external_client: reqwest::Client, pub device_type: DeviceType, } @@ -86,17 +89,28 @@ impl Client { pub fn new(settings_input: Option) -> Self { let settings = settings_input.unwrap_or_default(); - let headers = header::HeaderMap::new(); + fn new_client_builder() -> reqwest::ClientBuilder { + #[allow(unused_mut)] + let mut client_builder = reqwest::Client::builder(); - #[allow(unused_mut)] - let mut client_builder = reqwest::Client::builder().default_headers(headers); + #[cfg(all(not(target_os = "android"), not(target_arch = "wasm32")))] + { + client_builder = + client_builder.use_preconfigured_tls(rustls_platform_verifier::tls_config()); + } - #[cfg(all(not(target_os = "android"), not(target_arch = "wasm32")))] - { - client_builder = - client_builder.use_preconfigured_tls(rustls_platform_verifier::tls_config()); + client_builder } + let external_client = new_client_builder().build().unwrap(); + + let mut headers = header::HeaderMap::new(); + headers.append( + "Device-Type", + HeaderValue::from_str(&(settings.device_type as u8).to_string()).unwrap(), + ); + let client_builder = new_client_builder().default_headers(headers); + let client = client_builder.build().unwrap(); let identity = bitwarden_api_identity::apis::configuration::Configuration { @@ -127,6 +141,7 @@ impl Client { __api_configurations: ApiConfigurations { identity, api, + external_client, device_type: settings.device_type, }, encryption_settings: None, @@ -142,7 +157,7 @@ impl Client { #[cfg(feature = "mobile")] pub(crate) fn get_http_client(&self) -> &reqwest::Client { - &self.__api_configurations.api.client + &self.__api_configurations.external_client } #[cfg(feature = "secrets")] diff --git a/crates/bitwarden/src/mobile/crypto.rs b/crates/bitwarden/src/mobile/crypto.rs index 9b431b4b4..37bb3b905 100644 --- a/crates/bitwarden/src/mobile/crypto.rs +++ b/crates/bitwarden/src/mobile/crypto.rs @@ -54,14 +54,31 @@ pub enum InitUserCryptoMethod { AuthRequest { /// Private Key generated by the `crate::auth::new_auth_request`. request_private_key: String, + + method: AuthRequestMethod, + }, +} + +#[cfg(feature = "internal")] +#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +#[cfg_attr(feature = "mobile", derive(uniffi::Enum))] +pub enum AuthRequestMethod { + UserKey { /// User Key protected by the private key provided in `AuthRequestResponse`. protected_user_key: AsymmetricEncString, }, + MasterKey { + /// Master Key protected by the private key provided in `AuthRequestResponse`. + protected_master_key: AsymmetricEncString, + /// User Key protected by the MasterKey, provided by the auth response. + auth_request_key: EncString, + }, } #[cfg(feature = "internal")] pub async fn initialize_user_crypto(client: &mut Client, req: InitUserCryptoRequest) -> Result<()> { - use crate::auth::auth_request_decrypt_user_key; + use crate::auth::{auth_request_decrypt_master_key, auth_request_decrypt_user_key}; let login_method = crate::client::LoginMethod::User(crate::client::UserLoginMethod::Username { client_id: "".to_string(), @@ -89,9 +106,21 @@ pub async fn initialize_user_crypto(client: &mut Client, req: InitUserCryptoRequ } InitUserCryptoMethod::AuthRequest { request_private_key, - protected_user_key, + method, } => { - let user_key = auth_request_decrypt_user_key(request_private_key, protected_user_key)?; + let user_key = match method { + AuthRequestMethod::UserKey { protected_user_key } => { + auth_request_decrypt_user_key(request_private_key, protected_user_key)? + } + AuthRequestMethod::MasterKey { + protected_master_key, + auth_request_key, + } => auth_request_decrypt_master_key( + request_private_key, + protected_master_key, + auth_request_key, + )?, + }; client.initialize_user_crypto_decrypted_key(user_key, private_key)?; } } diff --git a/crates/bw/src/auth/login.rs b/crates/bw/src/auth/login.rs index 53b9b609e..e0195f5aa 100644 --- a/crates/bw/src/auth/login.rs +++ b/crates/bw/src/auth/login.rs @@ -114,3 +114,26 @@ pub(crate) async fn login_api_key( Ok(()) } + +pub(crate) async fn login_device( + mut client: Client, + email: Option, + device_identifier: Option, +) -> Result<()> { + let email = text_prompt_when_none("Email", email)?; + let device_identifier = text_prompt_when_none("Device Identifier", device_identifier)?; + + let auth = client + .auth() + .login_device(email, device_identifier) + .await + .unwrap(); + + println!("Fingerprint: {}", auth.fingerprint); + + Text::new("Press enter once approved").prompt()?; + + client.auth().login_device_complete(auth).await.unwrap(); + + Ok(()) +} diff --git a/crates/bw/src/auth/mod.rs b/crates/bw/src/auth/mod.rs index a4c7e2ed5..1f165f5f3 100644 --- a/crates/bw/src/auth/mod.rs +++ b/crates/bw/src/auth/mod.rs @@ -1,2 +1,2 @@ mod login; -pub(crate) use login::{login_api_key, login_password}; +pub(crate) use login::{login_api_key, login_device, login_password}; diff --git a/crates/bw/src/main.rs b/crates/bw/src/main.rs index 0e7cd975e..6674bda1e 100644 --- a/crates/bw/src/main.rs +++ b/crates/bw/src/main.rs @@ -78,6 +78,11 @@ enum LoginCommands { client_id: Option, client_secret: Option, }, + Device { + #[arg(short = 'e', long, help = "Email address")] + email: Option, + device_identifier: Option, + }, } #[derive(Subcommand, Clone)] @@ -163,6 +168,12 @@ async fn process_commands() -> Result<()> { client_id, client_secret, } => auth::login_api_key(client, client_id, client_secret).await?, + LoginCommands::Device { + email, + device_identifier, + } => { + auth::login_device(client, email, device_identifier).await?; + } } return Ok(()); } diff --git a/languages/kotlin/doc.md b/languages/kotlin/doc.md index f85222738..d69e134c4 100644 --- a/languages/kotlin/doc.md +++ b/languages/kotlin/doc.md @@ -1280,9 +1280,9 @@ implementations. Private Key generated by the `crate::auth::new_auth_request`. - protected_user_key + method + - User Key protected by the private key provided in `AuthRequestResponse`. From 6749058b7a2f196acff18d97eaae6c92a166fe8c Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Thu, 1 Feb 2024 16:47:59 +0100 Subject: [PATCH 18/34] Write more tests for Crypto crate (#570) Extend unit testing for the crypto crate. --- .../src/enc_string/asymmetric.rs | 33 ++++++++ crates/bitwarden-crypto/src/enc_string/mod.rs | 53 +++++++++++++ .../src/enc_string/symmetric.rs | 76 +++++++++++++++++++ 3 files changed, 162 insertions(+) diff --git a/crates/bitwarden-crypto/src/enc_string/asymmetric.rs b/crates/bitwarden-crypto/src/enc_string/asymmetric.rs index 04768db9e..f9bda838a 100644 --- a/crates/bitwarden-crypto/src/enc_string/asymmetric.rs +++ b/crates/bitwarden-crypto/src/enc_string/asymmetric.rs @@ -206,6 +206,8 @@ impl schemars::JsonSchema for AsymmetricEncString { #[cfg(test)] mod tests { + use schemars::schema_for; + use super::{AsymmetricCryptoKey, AsymmetricEncString, KeyDecryptable}; const RSA_PRIVATE_KEY: &str = "-----BEGIN PRIVATE KEY----- @@ -288,4 +290,35 @@ XKZBokBGnjFnTnKcs7nv/O8= assert_eq!(t.key.to_string(), cipher); assert_eq!(serde_json::to_string(&t).unwrap(), serialized); } + + #[test] + fn test_from_str_invalid() { + let enc_str = "7.ABC"; + let enc_string: Result = enc_str.parse(); + + let err = enc_string.unwrap_err(); + assert_eq!( + err.to_string(), + "EncString error, Invalid asymmetric type, got type 7 with 1 parts" + ); + } + + #[test] + fn test_debug_format() { + let enc_str: &str = "4.ZheRb3PCfAunyFdQYPfyrFqpuvmln9H9w5nDjt88i5A7ug1XE0LJdQHCIYJl0YOZ1gCOGkhFu/CRY2StiLmT3iRKrrVBbC1+qRMjNNyDvRcFi91LWsmRXhONVSPjywzrJJXglsztDqGkLO93dKXNhuKpcmtBLsvgkphk/aFvxbaOvJ/FHdK/iV0dMGNhc/9tbys8laTdwBlI5xIChpRcrfH+XpSFM88+Bu03uK67N9G6eU1UmET+pISJwJvMuIDMqH+qkT7OOzgL3t6I0H2LDj+CnsumnQmDsvQzDiNfTR0IgjpoE9YH2LvPXVP2wVUkiTwXD9cG/E7XeoiduHyHjw=="; + let enc_string: AsymmetricEncString = enc_str.parse().unwrap(); + + let debug_string = format!("{:?}", enc_string); + assert_eq!(debug_string, "AsymmetricEncString"); + } + + #[test] + fn test_json_schema() { + let schema = schema_for!(AsymmetricEncString); + + assert_eq!( + serde_json::to_string(&schema).unwrap(), + r#"{"$schema":"http://json-schema.org/draft-07/schema#","title":"AsymmetricEncString","type":"string"}"# + ); + } } diff --git a/crates/bitwarden-crypto/src/enc_string/mod.rs b/crates/bitwarden-crypto/src/enc_string/mod.rs index e1433821f..3250c1a58 100644 --- a/crates/bitwarden-crypto/src/enc_string/mod.rs +++ b/crates/bitwarden-crypto/src/enc_string/mod.rs @@ -75,3 +75,56 @@ where T::from_str(v).map_err(|e| E::custom(format!("{:?}", e))) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_check_length_less_than_expected() { + let buf = [1, 2, 3]; + let expected = 5; + let result = check_length(&buf, expected); + assert!(result.is_err()); + } + + #[test] + fn test_check_length_equal_to_expected() { + let buf = [1, 2, 3, 4, 5]; + let expected = 5; + let result = check_length(&buf, expected); + assert!(result.is_ok()); + } + + #[test] + fn test_check_length_greater_than_expected() { + let buf = [1, 2, 3, 4, 5, 6]; + let expected = 5; + let result = check_length(&buf, expected); + assert!(result.is_ok()); + } + + #[test] + fn test_split_enc_string_new_format() { + let s = "2.abc|def|ghi"; + let (header, parts) = split_enc_string(s); + assert_eq!(header, "2"); + assert_eq!(parts, vec!["abc", "def", "ghi"]); + } + + #[test] + fn test_split_enc_string_old_format_three_parts() { + let s = "abc|def|ghi"; + let (header, parts) = split_enc_string(s); + assert_eq!(header, "1"); + assert_eq!(parts, vec!["abc", "def", "ghi"]); + } + + #[test] + fn test_split_enc_string_old_format_fewer_parts() { + let s = "abc|def"; + let (header, parts) = split_enc_string(s); + assert_eq!(header, "0"); + assert_eq!(parts, vec!["abc", "def"]); + } +} diff --git a/crates/bitwarden-crypto/src/enc_string/symmetric.rs b/crates/bitwarden-crypto/src/enc_string/symmetric.rs index a76315e96..a7768de23 100644 --- a/crates/bitwarden-crypto/src/enc_string/symmetric.rs +++ b/crates/bitwarden-crypto/src/enc_string/symmetric.rs @@ -274,6 +274,8 @@ impl schemars::JsonSchema for EncString { #[cfg(test)] mod tests { + use schemars::schema_for; + use super::EncString; use crate::{derive_symmetric_key, KeyDecryptable, KeyEncryptable}; @@ -325,4 +327,78 @@ mod tests { assert_eq!(enc_string_new.to_string(), enc_str) } + + #[test] + fn test_from_str_cbc256() { + let enc_str = "0.pMS6/icTQABtulw52pq2lg==|XXbxKxDTh+mWiN1HjH2N1w=="; + let enc_string: EncString = enc_str.parse().unwrap(); + + assert_eq!(enc_string.enc_type(), 0); + if let EncString::AesCbc256_B64 { iv, data } = &enc_string { + assert_eq!( + iv, + &[164, 196, 186, 254, 39, 19, 64, 0, 109, 186, 92, 57, 218, 154, 182, 150] + ); + assert_eq!( + data, + &[93, 118, 241, 43, 16, 211, 135, 233, 150, 136, 221, 71, 140, 125, 141, 215] + ); + } + } + + #[test] + fn test_from_str_cbc128_hmac() { + let enc_str = "1.Hh8gISIjJCUmJygpKissLQ==|MjM0NTY3ODk6Ozw9Pj9AQUJDREU=|KCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4/QEFCQ0RFRkc="; + let enc_string: EncString = enc_str.parse().unwrap(); + + assert_eq!(enc_string.enc_type(), 1); + if let EncString::AesCbc128_HmacSha256_B64 { iv, mac, data } = &enc_string { + assert_eq!( + iv, + &[30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45] + ); + assert_eq!( + mac, + &[ + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71 + ] + ); + assert_eq!( + data, + &[50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69] + ); + } + } + + #[test] + fn test_from_str_invalid() { + let enc_str = "7.ABC"; + let enc_string: Result = enc_str.parse(); + + let err = enc_string.unwrap_err(); + assert_eq!( + err.to_string(), + "EncString error, Invalid symmetric type, got type 7 with 1 parts" + ); + } + + #[test] + fn test_debug_format() { + let enc_str = "2.pMS6/icTQABtulw52pq2lg==|XXbxKxDTh+mWiN1HjH2N1w==|Q6PkuT+KX/axrgN9ubD5Ajk2YNwxQkgs3WJM0S0wtG8="; + let enc_string: EncString = enc_str.parse().unwrap(); + + let debug_string = format!("{:?}", enc_string); + assert_eq!(debug_string, "EncString"); + } + + #[test] + fn test_json_schema() { + let schema = schema_for!(EncString); + + assert_eq!( + serde_json::to_string(&schema).unwrap(), + r#"{"$schema":"http://json-schema.org/draft-07/schema#","title":"EncString","type":"string"}"# + ); + } } From ac58ac9becb16fbf28862a02783fc4140a1c04f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa?= Date: Fri, 2 Feb 2024 12:06:04 +0100 Subject: [PATCH 19/34] Add test to ensure we are using a compatible rustls version (#569) ## Type of change ``` - [x] Bug fix - [ ] New feature development - [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc) - [ ] Build/deploy pipeline (DevOps) - [ ] Other ``` ## Objective We need to keep the version of rustls that reqwest and rustls-platform-verifier use in sync to avoid runtime errors when creating the reqwest client, we can't check it at compile time due to the use of `&dyn Any` and downcasting, but we can add a test. --- crates/bitwarden/src/client/client.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/crates/bitwarden/src/client/client.rs b/crates/bitwarden/src/client/client.rs index b8632e21e..5b2c8b7e0 100644 --- a/crates/bitwarden/src/client/client.rs +++ b/crates/bitwarden/src/client/client.rs @@ -306,3 +306,23 @@ impl Client { Ok(self.encryption_settings.as_ref().unwrap()) } } + +#[cfg(test)] +mod tests { + #[test] + fn test_reqwest_rustls_platform_verifier_are_compatible() { + // rustls-platform-verifier is generating a rustls::ClientConfig, + // which reqwest accepts as a &dyn Any and then downcasts it to a + // rustls::ClientConfig. + + // This means that if the rustls version of the two crates don't match, + // the downcast will fail and we will get a runtime error. + + // This tests is added to ensure that it doesn't happen. + + let _ = reqwest::ClientBuilder::new() + .use_preconfigured_tls(rustls_platform_verifier::tls_config()) + .build() + .unwrap(); + } +} From 32be569f753794884930bbc0e102313a7cc04487 Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Fri, 2 Feb 2024 13:36:41 +0100 Subject: [PATCH 20/34] Support validating password by decrypting user key (#572) Enables password validation without knowing the password hash by decrypting the user key. --- crates/bitwarden-uniffi/src/auth/mod.rs | 23 +++- crates/bitwarden/src/auth/client_auth.rs | 15 ++- crates/bitwarden/src/auth/login/password.rs | 3 +- crates/bitwarden/src/auth/login/two_factor.rs | 3 +- crates/bitwarden/src/auth/mod.rs | 10 +- crates/bitwarden/src/auth/password/mod.rs | 2 + .../bitwarden/src/auth/password/validate.rs | 121 ++++++++++++++++-- 7 files changed, 153 insertions(+), 24 deletions(-) diff --git a/crates/bitwarden-uniffi/src/auth/mod.rs b/crates/bitwarden-uniffi/src/auth/mod.rs index 62c791967..75e0c5656 100644 --- a/crates/bitwarden-uniffi/src/auth/mod.rs +++ b/crates/bitwarden-uniffi/src/auth/mod.rs @@ -90,8 +90,27 @@ impl ClientAuth { .write() .await .auth() - .validate_password(password, password_hash.to_string()) - .await?) + .validate_password(password, password_hash.to_string())?) + } + + /// Validate the user password without knowing the password hash + /// + /// Used for accounts that we know have master passwords but that have not logged in with a + /// password. Some example are login with device or TDE. + /// + /// This works by comparing the provided password against the encrypted user key. + pub async fn validate_password_user_key( + &self, + password: String, + encrypted_user_key: String, + ) -> Result { + Ok(self + .0 + .0 + .write() + .await + .auth() + .validate_password_user_key(password, encrypted_user_key)?) } /// Initialize a new auth request diff --git a/crates/bitwarden/src/auth/client_auth.rs b/crates/bitwarden/src/auth/client_auth.rs index 0ae2f6f33..f08daf0b7 100644 --- a/crates/bitwarden/src/auth/client_auth.rs +++ b/crates/bitwarden/src/auth/client_auth.rs @@ -16,7 +16,8 @@ use crate::{ TwoFactorEmailRequest, }, password::{ - password_strength, satisfies_policy, validate_password, MasterPasswordPolicyOptions, + password_strength, satisfies_policy, validate_password, validate_password_user_key, + MasterPasswordPolicyOptions, }, register::{make_register_keys, register}, AuthRequestResponse, RegisterKeyResponse, RegisterRequest, @@ -101,8 +102,16 @@ impl<'a> ClientAuth<'a> { send_two_factor_email(self.client, tf).await } - pub async fn validate_password(&self, password: String, password_hash: String) -> Result { - validate_password(self.client, password, password_hash).await + pub fn validate_password(&self, password: String, password_hash: String) -> Result { + validate_password(self.client, password, password_hash) + } + + pub fn validate_password_user_key( + &self, + password: String, + encrypted_user_key: String, + ) -> Result { + validate_password_user_key(self.client, password, encrypted_user_key) } pub fn new_auth_request(&self, email: &str) -> Result { diff --git a/crates/bitwarden/src/auth/login/password.rs b/crates/bitwarden/src/auth/login/password.rs index 4c978e559..02552b70e 100644 --- a/crates/bitwarden/src/auth/login/password.rs +++ b/crates/bitwarden/src/auth/login/password.rs @@ -34,8 +34,7 @@ pub(crate) async fn login_password( &input.kdf, &input.password, HashPurpose::ServerAuthorization, - ) - .await?; + )?; let response = request_identity_tokens(client, input, &password_hash).await?; if let IdentityTokenResponse::Authenticated(r) = &response { diff --git a/crates/bitwarden/src/auth/login/two_factor.rs b/crates/bitwarden/src/auth/login/two_factor.rs index 45be042c7..c8f0cc55b 100644 --- a/crates/bitwarden/src/auth/login/two_factor.rs +++ b/crates/bitwarden/src/auth/login/two_factor.rs @@ -27,8 +27,7 @@ pub(crate) async fn send_two_factor_email( &kdf, &input.password, HashPurpose::ServerAuthorization, - ) - .await?; + )?; let config = client.get_api_configurations().await; bitwarden_api_api::apis::two_factor_api::two_factor_send_email_login_post( diff --git a/crates/bitwarden/src/auth/mod.rs b/crates/bitwarden/src/auth/mod.rs index d77847574..021c97c0f 100644 --- a/crates/bitwarden/src/auth/mod.rs +++ b/crates/bitwarden/src/auth/mod.rs @@ -23,7 +23,7 @@ pub(crate) use auth_request::{auth_request_decrypt_master_key, auth_request_decr use crate::{client::Kdf, error::Result}; #[cfg(feature = "internal")] -async fn determine_password_hash( +fn determine_password_hash( email: &str, kdf: &Kdf, password: &str, @@ -40,8 +40,8 @@ mod tests { use super::*; #[cfg(feature = "internal")] - #[tokio::test] - async fn test_determine_password_hash() { + #[test] + fn test_determine_password_hash() { use super::determine_password_hash; let password = "password123"; @@ -51,9 +51,7 @@ mod tests { }; let purpose = HashPurpose::LocalAuthorization; - let result = determine_password_hash(email, &kdf, password, purpose) - .await - .unwrap(); + let result = determine_password_hash(email, &kdf, password, purpose).unwrap(); assert_eq!(result, "7kTqkF1pY/3JeOu73N9kR99fDDe9O1JOZaVc7KH3lsU="); } diff --git a/crates/bitwarden/src/auth/password/mod.rs b/crates/bitwarden/src/auth/password/mod.rs index b7833c7f8..d0f3329f2 100644 --- a/crates/bitwarden/src/auth/password/mod.rs +++ b/crates/bitwarden/src/auth/password/mod.rs @@ -3,5 +3,7 @@ pub(crate) use policy::satisfies_policy; pub use policy::MasterPasswordPolicyOptions; mod validate; pub(crate) use validate::validate_password; +#[cfg(feature = "internal")] +pub(crate) use validate::validate_password_user_key; mod strength; pub(crate) use strength::password_strength; diff --git a/crates/bitwarden/src/auth/password/validate.rs b/crates/bitwarden/src/auth/password/validate.rs index f6d22e11a..9003347d9 100644 --- a/crates/bitwarden/src/auth/password/validate.rs +++ b/crates/bitwarden/src/auth/password/validate.rs @@ -1,4 +1,4 @@ -use bitwarden_crypto::HashPurpose; +use bitwarden_crypto::{HashPurpose, MasterKey}; use crate::{ auth::determine_password_hash, @@ -8,7 +8,7 @@ use crate::{ }; /// Validate if the provided password matches the password hash stored in the client. -pub(crate) async fn validate_password( +pub(crate) fn validate_password( client: &Client, password: String, password_hash: String, @@ -22,9 +22,12 @@ pub(crate) async fn validate_password( match login_method { UserLoginMethod::Username { email, kdf, .. } | UserLoginMethod::ApiKey { email, kdf, .. } => { - let hash = - determine_password_hash(email, kdf, &password, HashPurpose::LocalAuthorization) - .await?; + let hash = determine_password_hash( + email, + kdf, + &password, + HashPurpose::LocalAuthorization, + )?; Ok(hash == password_hash) } @@ -34,13 +37,53 @@ pub(crate) async fn validate_password( } } +#[cfg(feature = "internal")] +pub(crate) fn validate_password_user_key( + client: &Client, + password: String, + encrypted_user_key: String, +) -> Result { + let login_method = client + .login_method + .as_ref() + .ok_or(Error::NotAuthenticated)?; + + if let LoginMethod::User(login_method) = login_method { + match login_method { + UserLoginMethod::Username { email, kdf, .. } + | UserLoginMethod::ApiKey { email, kdf, .. } => { + let master_key = MasterKey::derive(password.as_bytes(), email.as_bytes(), kdf)?; + let user_key = master_key + .decrypt_user_key(encrypted_user_key.parse()?) + .map_err(|_| "wrong password")?; + + let enc = client + .get_encryption_settings() + .map_err(|_| Error::VaultLocked)?; + + let existing_key = enc.get_key(&None).ok_or(Error::VaultLocked)?; + + if user_key.to_vec() != existing_key.to_vec() { + return Err("wrong user key".into()); + } + + Ok(master_key + .derive_master_key_hash(password.as_bytes(), HashPurpose::LocalAuthorization)?) + } + } + } else { + Err(Error::NotAuthenticated) + } +} + #[cfg(test)] mod tests { - #[tokio::test] - async fn test_validate_password() { + use crate::auth::password::{validate::validate_password_user_key, validate_password}; + + #[test] + fn test_validate_password() { use std::num::NonZeroU32; - use super::validate_password; use crate::client::{Client, Kdf, LoginMethod, UserLoginMethod}; let mut client = Client::new(None); @@ -55,8 +98,68 @@ mod tests { let password = "password123".to_string(); let password_hash = "7kTqkF1pY/3JeOu73N9kR99fDDe9O1JOZaVc7KH3lsU=".to_string(); - let result = validate_password(&client, password, password_hash).await; + let result = validate_password(&client, password, password_hash); assert!(result.unwrap()); } + + #[cfg(feature = "internal")] + #[test] + fn test_validate_password_user_key() { + use std::num::NonZeroU32; + + use crate::client::{Client, Kdf, LoginMethod, UserLoginMethod}; + + let mut client = Client::new(None); + client.set_login_method(LoginMethod::User(UserLoginMethod::Username { + email: "test@bitwarden.com".to_string(), + kdf: Kdf::PBKDF2 { + iterations: NonZeroU32::new(600_000).unwrap(), + }, + client_id: "1".to_string(), + })); + + let user_key = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE="; + let private_key = "2.yN7l00BOlUE0Sb0M//Q53w==|EwKG/BduQRQ33Izqc/ogoBROIoI5dmgrxSo82sgzgAMIBt3A2FZ9vPRMY+GWT85JiqytDitGR3TqwnFUBhKUpRRAq4x7rA6A1arHrFp5Tp1p21O3SfjtvB3quiOKbqWk6ZaU1Np9HwqwAecddFcB0YyBEiRX3VwF2pgpAdiPbSMuvo2qIgyob0CUoC/h4Bz1be7Qa7B0Xw9/fMKkB1LpOm925lzqosyMQM62YpMGkjMsbZz0uPopu32fxzDWSPr+kekNNyLt9InGhTpxLmq1go/pXR2uw5dfpXc5yuta7DB0EGBwnQ8Vl5HPdDooqOTD9I1jE0mRyuBpWTTI3FRnu3JUh3rIyGBJhUmHqGZvw2CKdqHCIrQeQkkEYqOeJRJVdBjhv5KGJifqT3BFRwX/YFJIChAQpebNQKXe/0kPivWokHWwXlDB7S7mBZzhaAPidZvnuIhalE2qmTypDwHy22FyqV58T8MGGMchcASDi/QXI6kcdpJzPXSeU9o+NC68QDlOIrMVxKFeE7w7PvVmAaxEo0YwmuAzzKy9QpdlK0aab/xEi8V4iXj4hGepqAvHkXIQd+r3FNeiLfllkb61p6WTjr5urcmDQMR94/wYoilpG5OlybHdbhsYHvIzYoLrC7fzl630gcO6t4nM24vdB6Ymg9BVpEgKRAxSbE62Tqacxqnz9AcmgItb48NiR/He3n3ydGjPYuKk/ihZMgEwAEZvSlNxYONSbYrIGDtOY+8Nbt6KiH3l06wjZW8tcmFeVlWv+tWotnTY9IqlAfvNVTjtsobqtQnvsiDjdEVtNy/s2ci5TH+NdZluca2OVEr91Wayxh70kpM6ib4UGbfdmGgCo74gtKvKSJU0rTHakQ5L9JlaSDD5FamBRyI0qfL43Ad9qOUZ8DaffDCyuaVyuqk7cz9HwmEmvWU3VQ+5t06n/5kRDXttcw8w+3qClEEdGo1KeENcnXCB32dQe3tDTFpuAIMLqwXs6FhpawfZ5kPYvLPczGWaqftIs/RXJ/EltGc0ugw2dmTLpoQhCqrcKEBDoYVk0LDZKsnzitOGdi9mOWse7Se8798ib1UsHFUjGzISEt6upestxOeupSTOh0v4+AjXbDzRUyogHww3V+Bqg71bkcMxtB+WM+pn1XNbVTyl9NR040nhP7KEf6e9ruXAtmrBC2ah5cFEpLIot77VFZ9ilLuitSz+7T8n1yAh1IEG6xxXxninAZIzi2qGbH69O5RSpOJuJTv17zTLJQIIc781JwQ2TTwTGnx5wZLbffhCasowJKd2EVcyMJyhz6ru0PvXWJ4hUdkARJs3Xu8dus9a86N8Xk6aAPzBDqzYb1vyFIfBxP0oO8xFHgd30Cgmz8UrSE3qeWRrF8ftrI6xQnFjHBGWD/JWSvd6YMcQED0aVuQkuNW9ST/DzQThPzRfPUoiL10yAmV7Ytu4fR3x2sF0Yfi87YhHFuCMpV/DsqxmUizyiJuD938eRcH8hzR/VO53Qo3UIsqOLcyXtTv6THjSlTopQ+JOLOnHm1w8dzYbLN44OG44rRsbihMUQp+wUZ6bsI8rrOnm9WErzkbQFbrfAINdoCiNa6cimYIjvvnMTaFWNymqY1vZxGztQiMiHiHYwTfwHTXrb9j0uPM=|09J28iXv9oWzYtzK2LBT6Yht4IT4MijEkk0fwFdrVQ4=".parse().unwrap(); + + client + .initialize_user_crypto("asdfasdfasdf", user_key.parse().unwrap(), private_key) + .unwrap(); + + let result = + validate_password_user_key(&client, "asdfasdfasdf".to_string(), user_key.to_string()) + .unwrap(); + + assert_eq!(result, "aOvkBXFhSdgrBWR3hZCMRoML9+h5yRblU3lFphCdkeA="); + assert!(validate_password(&client, "asdfasdfasdf".to_string(), result.to_string()).unwrap()) + } + + #[cfg(feature = "internal")] + #[test] + fn test_validate_password_user_key_wrong_password() { + use std::num::NonZeroU32; + + use crate::client::{Client, Kdf, LoginMethod, UserLoginMethod}; + + let mut client = Client::new(None); + client.set_login_method(LoginMethod::User(UserLoginMethod::Username { + email: "test@bitwarden.com".to_string(), + kdf: Kdf::PBKDF2 { + iterations: NonZeroU32::new(600_000).unwrap(), + }, + client_id: "1".to_string(), + })); + + let user_key = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE="; + let private_key = "2.yN7l00BOlUE0Sb0M//Q53w==|EwKG/BduQRQ33Izqc/ogoBROIoI5dmgrxSo82sgzgAMIBt3A2FZ9vPRMY+GWT85JiqytDitGR3TqwnFUBhKUpRRAq4x7rA6A1arHrFp5Tp1p21O3SfjtvB3quiOKbqWk6ZaU1Np9HwqwAecddFcB0YyBEiRX3VwF2pgpAdiPbSMuvo2qIgyob0CUoC/h4Bz1be7Qa7B0Xw9/fMKkB1LpOm925lzqosyMQM62YpMGkjMsbZz0uPopu32fxzDWSPr+kekNNyLt9InGhTpxLmq1go/pXR2uw5dfpXc5yuta7DB0EGBwnQ8Vl5HPdDooqOTD9I1jE0mRyuBpWTTI3FRnu3JUh3rIyGBJhUmHqGZvw2CKdqHCIrQeQkkEYqOeJRJVdBjhv5KGJifqT3BFRwX/YFJIChAQpebNQKXe/0kPivWokHWwXlDB7S7mBZzhaAPidZvnuIhalE2qmTypDwHy22FyqV58T8MGGMchcASDi/QXI6kcdpJzPXSeU9o+NC68QDlOIrMVxKFeE7w7PvVmAaxEo0YwmuAzzKy9QpdlK0aab/xEi8V4iXj4hGepqAvHkXIQd+r3FNeiLfllkb61p6WTjr5urcmDQMR94/wYoilpG5OlybHdbhsYHvIzYoLrC7fzl630gcO6t4nM24vdB6Ymg9BVpEgKRAxSbE62Tqacxqnz9AcmgItb48NiR/He3n3ydGjPYuKk/ihZMgEwAEZvSlNxYONSbYrIGDtOY+8Nbt6KiH3l06wjZW8tcmFeVlWv+tWotnTY9IqlAfvNVTjtsobqtQnvsiDjdEVtNy/s2ci5TH+NdZluca2OVEr91Wayxh70kpM6ib4UGbfdmGgCo74gtKvKSJU0rTHakQ5L9JlaSDD5FamBRyI0qfL43Ad9qOUZ8DaffDCyuaVyuqk7cz9HwmEmvWU3VQ+5t06n/5kRDXttcw8w+3qClEEdGo1KeENcnXCB32dQe3tDTFpuAIMLqwXs6FhpawfZ5kPYvLPczGWaqftIs/RXJ/EltGc0ugw2dmTLpoQhCqrcKEBDoYVk0LDZKsnzitOGdi9mOWse7Se8798ib1UsHFUjGzISEt6upestxOeupSTOh0v4+AjXbDzRUyogHww3V+Bqg71bkcMxtB+WM+pn1XNbVTyl9NR040nhP7KEf6e9ruXAtmrBC2ah5cFEpLIot77VFZ9ilLuitSz+7T8n1yAh1IEG6xxXxninAZIzi2qGbH69O5RSpOJuJTv17zTLJQIIc781JwQ2TTwTGnx5wZLbffhCasowJKd2EVcyMJyhz6ru0PvXWJ4hUdkARJs3Xu8dus9a86N8Xk6aAPzBDqzYb1vyFIfBxP0oO8xFHgd30Cgmz8UrSE3qeWRrF8ftrI6xQnFjHBGWD/JWSvd6YMcQED0aVuQkuNW9ST/DzQThPzRfPUoiL10yAmV7Ytu4fR3x2sF0Yfi87YhHFuCMpV/DsqxmUizyiJuD938eRcH8hzR/VO53Qo3UIsqOLcyXtTv6THjSlTopQ+JOLOnHm1w8dzYbLN44OG44rRsbihMUQp+wUZ6bsI8rrOnm9WErzkbQFbrfAINdoCiNa6cimYIjvvnMTaFWNymqY1vZxGztQiMiHiHYwTfwHTXrb9j0uPM=|09J28iXv9oWzYtzK2LBT6Yht4IT4MijEkk0fwFdrVQ4=".parse().unwrap(); + + client + .initialize_user_crypto("asdfasdfasdf", user_key.parse().unwrap(), private_key) + .unwrap(); + + let result = validate_password_user_key(&client, "abc".to_string(), user_key.to_string()) + .unwrap_err(); + + assert_eq!(result.to_string(), "Internal error: wrong password"); + } } From 0be34620bfa94def4d09cab515ca287b9a6ed7ce Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 10:41:05 +0100 Subject: [PATCH 21/34] [deps]: Update Rust crate toml to 0.8.9 (#577) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [toml](https://togithub.com/toml-rs/toml) | dependencies | patch | `0.8.8` -> `0.8.9` | --- ### Release Notes
toml-rs/toml (toml) ### [`v0.8.9`](https://togithub.com/toml-rs/toml/compare/toml-v0.8.8...toml-v0.8.9) [Compare Source](https://togithub.com/toml-rs/toml/compare/toml-v0.8.8...toml-v0.8.9)
--- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/bitwarden/sdk). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Cargo.lock | 10 +++++----- crates/bws/Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8ebf52136..f7e721c1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -612,7 +612,7 @@ dependencies = [ "tempfile", "thiserror", "tokio", - "toml 0.8.8", + "toml 0.8.9", "uuid", ] @@ -3452,9 +3452,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +checksum = "c6a4b9e8023eb94392d3dca65d717c53abc5dad49c07cb65bb8fcd87115fa325" dependencies = [ "serde", "serde_spanned", @@ -3473,9 +3473,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ "indexmap 2.2.1", "serde", diff --git a/crates/bws/Cargo.toml b/crates/bws/Cargo.toml index 1c04a6668..bb757eca6 100644 --- a/crates/bws/Cargo.toml +++ b/crates/bws/Cargo.toml @@ -37,7 +37,7 @@ serde_yaml = "0.9" supports-color = "2.1.0" thiserror = "1.0.56" tokio = { version = "1.35.1", features = ["rt-multi-thread", "macros"] } -toml = "0.8.8" +toml = "0.8.9" uuid = { version = "^1.7.0", features = ["serde"] } bitwarden = { path = "../bitwarden", version = "0.4.0", features = ["secrets"] } From ddd98880cf6ee026ab38cb0446827ef5351e89b5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 11:01:34 +0100 Subject: [PATCH 22/34] [deps]: Update Rust crate itertools to 0.12.1 (#576) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [itertools](https://togithub.com/rust-itertools/itertools) | dependencies | patch | `0.12.0` -> `0.12.1` | --- ### Release Notes
rust-itertools/itertools (itertools) ### [`v0.12.1`](https://togithub.com/rust-itertools/itertools/blob/HEAD/CHANGELOG.md#0121) [Compare Source](https://togithub.com/rust-itertools/itertools/compare/v0.12.0...v0.12.1) ##### Added - Documented iteration order guarantee for `Itertools::[tuple_]combinations` ([#​822](https://togithub.com/rust-itertools/itertools/issues/822)) - Documented possible panic in `iterate` ([#​842](https://togithub.com/rust-itertools/itertools/issues/842)) - Implemented `Clone` and `Debug` for `Diff` ([#​845](https://togithub.com/rust-itertools/itertools/issues/845)) - Implemented `Debug` for `WithPosition` ([#​859](https://togithub.com/rust-itertools/itertools/issues/859)) - Implemented `Eq` for `MinMaxResult` ([#​838](https://togithub.com/rust-itertools/itertools/issues/838)) - Implemented `From>` for `Option>` ([#​843](https://togithub.com/rust-itertools/itertools/issues/843)) - Implemented `PeekingNext` for `RepeatN` ([#​855](https://togithub.com/rust-itertools/itertools/issues/855)) ##### Changed - Made `CoalesceBy` lazy ([#​801](https://togithub.com/rust-itertools/itertools/issues/801)) - Optimized `Filter[Map]Ok::next`, `Itertools::partition`, `Unique[By]::next[_back]` ([#​818](https://togithub.com/rust-itertools/itertools/issues/818)) - Optimized `Itertools::find_position` ([#​837](https://togithub.com/rust-itertools/itertools/issues/837)) - Optimized `Positions::next[_back]` ([#​816](https://togithub.com/rust-itertools/itertools/issues/816)) - Optimized `ZipLongest::fold` ([#​854](https://togithub.com/rust-itertools/itertools/issues/854)) - Relaxed `Debug` bounds for `GroupingMapBy` ([#​860](https://togithub.com/rust-itertools/itertools/issues/860)) - Specialized `ExactlyOneError::fold` ([#​826](https://togithub.com/rust-itertools/itertools/issues/826)) - Specialized `Interleave[Shortest]::fold` ([#​849](https://togithub.com/rust-itertools/itertools/issues/849)) - Specialized `MultiPeek::fold` ([#​820](https://togithub.com/rust-itertools/itertools/issues/820)) - Specialized `PadUsing::[r]fold` ([#​825](https://togithub.com/rust-itertools/itertools/issues/825)) - Specialized `PeekNth::fold` ([#​824](https://togithub.com/rust-itertools/itertools/issues/824)) - Specialized `Positions::[r]fold` ([#​813](https://togithub.com/rust-itertools/itertools/issues/813)) - Specialized `PutBackN::fold` ([#​823](https://togithub.com/rust-itertools/itertools/issues/823)) - Specialized `RepeatN::[r]fold` ([#​821](https://togithub.com/rust-itertools/itertools/issues/821)) - Specialized `TakeWhileInclusive::fold` ([#​851](https://togithub.com/rust-itertools/itertools/issues/851)) - Specialized `ZipLongest::rfold` ([#​848](https://togithub.com/rust-itertools/itertools/issues/848)) ##### Notable Internal Changes - Added test coverage in CI ([#​847](https://togithub.com/rust-itertools/itertools/issues/847), [#​856](https://togithub.com/rust-itertools/itertools/issues/856)) - Added semver check in CI ([#​784](https://togithub.com/rust-itertools/itertools/issues/784)) - Enforced `clippy` in CI ([#​740](https://togithub.com/rust-itertools/itertools/issues/740)) - Enforced `rustdoc` in CI ([#​840](https://togithub.com/rust-itertools/itertools/issues/840)) - Improved specialization tests ([#​807](https://togithub.com/rust-itertools/itertools/issues/807)) - More specialization benchmarks ([#​806](https://togithub.com/rust-itertools/itertools/issues/806))
--- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/bitwarden/sdk). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Cargo.lock | 6 +++--- crates/sdk-schemas/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f7e721c1a..03f0cfc2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1806,9 +1806,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ "either", ] @@ -2893,7 +2893,7 @@ dependencies = [ "bitwarden", "bitwarden-json", "bitwarden-uniffi", - "itertools 0.12.0", + "itertools 0.12.1", "schemars", "serde_json", ] diff --git a/crates/sdk-schemas/Cargo.toml b/crates/sdk-schemas/Cargo.toml index 3e7282e7e..81a9d76ca 100644 --- a/crates/sdk-schemas/Cargo.toml +++ b/crates/sdk-schemas/Cargo.toml @@ -13,7 +13,7 @@ internal = [ [dependencies] anyhow = "1.0.79" -itertools = "0.12.0" +itertools = "0.12.1" schemars = { version = "0.8.16", features = ["preserve_order"] } serde_json = "1.0.113" From 098870d2b02d1a3abee461c0e9e318a0ecd829dc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 11:02:11 +0100 Subject: [PATCH 23/34] [deps]: Update Rust crate tokio to 1.36.0 (#579) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [tokio](https://tokio.rs) ([source](https://togithub.com/tokio-rs/tokio)) | dependencies | minor | `1.35.1` -> `1.36.0` | | [tokio](https://tokio.rs) ([source](https://togithub.com/tokio-rs/tokio)) | dev-dependencies | minor | `1.35.1` -> `1.36.0` | --- ### Release Notes
tokio-rs/tokio (tokio) ### [`v1.36.0`](https://togithub.com/tokio-rs/tokio/releases/tag/tokio-1.36.0): Tokio v1.36.0 [Compare Source](https://togithub.com/tokio-rs/tokio/compare/tokio-1.35.1...tokio-1.36.0) ##### 1.36.0 (February 2nd, 2024) ##### Added - io: add `tokio::io::Join` ([#​6220]) - io: implement `AsyncWrite` for `Empty` ([#​6235]) - net: add support for anonymous unix pipes ([#​6127]) - net: add `UnixSocket` ([#​6290]) - net: expose keepalive option on `TcpSocket` ([#​6311]) - sync: add `{Receiver,UnboundedReceiver}::poll_recv_many` ([#​6236]) - sync: add `Sender::{try_,}reserve_many` ([#​6205]) - sync: add `watch::Receiver::mark_unchanged` ([#​6252]) - task: add `JoinSet::try_join_next` ([#​6280]) - time: add `FutureExt::timeout` ([#​6276]) ##### Changed - io: make `copy` cooperative ([#​6265]) - io: make `repeat` and `sink` cooperative ([#​6254]) - io: simplify check for empty slice ([#​6293]) - rt: improve robustness of `wake_in_drop_after_panic` test ([#​6238]) - process: use pidfd on Linux when available ([#​6152]) - sync: use AtomicBool in broadcast channel future ([#​6298]) ##### Fixed - chore: typographic improvements ([#​6262]) - runtime: remove obsolete comment ([#​6303]) - task: fix typo ([#​6261]) ##### Documented - io: clarify `clear_ready` docs ([#​6304]) - net: document that `*Fd` traits on `TcpSocket` are unix-only ([#​6294]) - sync: document FIFO behavior of `tokio::sync::Mutex` ([#​6279]) [#​6220]: https://togithub.com/tokio-rs/tokio/pull/6220 [#​6235]: https://togithub.com/tokio-rs/tokio/pull/6235 [#​6127]: https://togithub.com/tokio-rs/tokio/pull/6127 [#​6290]: https://togithub.com/tokio-rs/tokio/pull/6290 [#​6311]: https://togithub.com/tokio-rs/tokio/pull/6311 [#​6236]: https://togithub.com/tokio-rs/tokio/pull/6236 [#​6205]: https://togithub.com/tokio-rs/tokio/pull/6205 [#​6252]: https://togithub.com/tokio-rs/tokio/pull/6252 [#​6280]: https://togithub.com/tokio-rs/tokio/pull/6280 [#​6276]: https://togithub.com/tokio-rs/tokio/pull/6276 [#​6265]: https://togithub.com/tokio-rs/tokio/pull/6265 [#​6254]: https://togithub.com/tokio-rs/tokio/pull/6254 [#​6293]: https://togithub.com/tokio-rs/tokio/pull/6293 [#​6238]: https://togithub.com/tokio-rs/tokio/pull/6238 [#​6152]: https://togithub.com/tokio-rs/tokio/pull/6152 [#​6298]: https://togithub.com/tokio-rs/tokio/pull/6298 [#​6262]: https://togithub.com/tokio-rs/tokio/pull/6262 [#​6303]: https://togithub.com/tokio-rs/tokio/pull/6303 [#​6261]: https://togithub.com/tokio-rs/tokio/pull/6261 [#​6304]: https://togithub.com/tokio-rs/tokio/pull/6304 [#​6294]: https://togithub.com/tokio-rs/tokio/pull/6294 [#​6279]: https://togithub.com/tokio-rs/tokio/pull/6279
--- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about these updates again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/bitwarden/sdk). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- crates/bitwarden-generators/Cargo.toml | 2 +- crates/bitwarden-py/Cargo.toml | 2 +- crates/bitwarden/Cargo.toml | 2 +- crates/bw/Cargo.toml | 2 +- crates/bws/Cargo.toml | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 03f0cfc2f..b4469fc04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3391,9 +3391,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.1" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", diff --git a/crates/bitwarden-generators/Cargo.toml b/crates/bitwarden-generators/Cargo.toml index 7f26a47cf..39eeb3d71 100644 --- a/crates/bitwarden-generators/Cargo.toml +++ b/crates/bitwarden-generators/Cargo.toml @@ -29,5 +29,5 @@ uniffi = { version = "=0.26.1", optional = true } [dev-dependencies] rand_chacha = "0.3.1" -tokio = { version = "1.35.1", features = ["rt", "macros"] } +tokio = { version = "1.36.0", features = ["rt", "macros"] } wiremock = "0.5.22" diff --git a/crates/bitwarden-py/Cargo.toml b/crates/bitwarden-py/Cargo.toml index ed7c5df5e..e81f2f5a8 100644 --- a/crates/bitwarden-py/Cargo.toml +++ b/crates/bitwarden-py/Cargo.toml @@ -18,7 +18,7 @@ bitwarden-json = { path = "../bitwarden-json", features = ["secrets"] } pyo3-build-config = { version = "0.20.2" } [target.'cfg(not(target_arch="wasm32"))'.dependencies] -tokio = { version = "1.35.1", features = ["rt-multi-thread", "macros"] } +tokio = { version = "1.36.0", features = ["rt-multi-thread", "macros"] } pyo3-asyncio = { version = "0.20.0", features = [ "attributes", "tokio-runtime", diff --git a/crates/bitwarden/Cargo.toml b/crates/bitwarden/Cargo.toml index b52e2f8f3..d9508258b 100644 --- a/crates/bitwarden/Cargo.toml +++ b/crates/bitwarden/Cargo.toml @@ -76,6 +76,6 @@ reqwest = { version = "*", features = [ [dev-dependencies] rand_chacha = "0.3.1" -tokio = { version = "1.35.1", features = ["rt", "macros"] } +tokio = { version = "1.36.0", features = ["rt", "macros"] } wiremock = "0.5.22" zeroize = { version = ">=1.7.0, <2.0", features = ["derive", "aarch64"] } diff --git a/crates/bw/Cargo.toml b/crates/bw/Cargo.toml index cc07397cd..1943e5ad5 100644 --- a/crates/bw/Cargo.toml +++ b/crates/bw/Cargo.toml @@ -18,7 +18,7 @@ color-eyre = "0.6" env_logger = "0.11.1" inquire = "0.6.2" log = "0.4.20" -tokio = { version = "1.35.1", features = ["rt-multi-thread", "macros"] } +tokio = { version = "1.36.0", features = ["rt-multi-thread", "macros"] } bitwarden = { path = "../bitwarden", version = "0.4.0", features = [ "internal", diff --git a/crates/bws/Cargo.toml b/crates/bws/Cargo.toml index bb757eca6..608db45a8 100644 --- a/crates/bws/Cargo.toml +++ b/crates/bws/Cargo.toml @@ -36,7 +36,7 @@ serde_json = "^1.0.113" serde_yaml = "0.9" supports-color = "2.1.0" thiserror = "1.0.56" -tokio = { version = "1.35.1", features = ["rt-multi-thread", "macros"] } +tokio = { version = "1.36.0", features = ["rt-multi-thread", "macros"] } toml = "0.8.9" uuid = { version = "^1.7.0", features = ["serde"] } From bf02c3ba5a6080c4d67765e01a7d6e85e49e1363 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 11:02:56 +0100 Subject: [PATCH 24/34] [deps]: Update Rust crate clap_complete to 4.4.10 (#575) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [clap_complete](https://togithub.com/clap-rs/clap) ([source](https://togithub.com/clap-rs/clap/tree/HEAD/clap_complete)) | dependencies | patch | `4.4.9` -> `4.4.10` | --- ### Release Notes
clap-rs/clap (clap_complete) ### [`v4.4.10`](https://togithub.com/clap-rs/clap/blob/HEAD/clap_complete/CHANGELOG.md#4410---2024-02-02) [Compare Source](https://togithub.com/clap-rs/clap/compare/clap_complete-v4.4.9...clap_complete-v4.4.10) ##### Fixes - *(bash)* Allow completing filenames with spaces
--- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/bitwarden/sdk). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- crates/bws/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b4469fc04..6cae436a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -752,9 +752,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.4.9" +version = "4.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df631ae429f6613fcd3a7c1adbdb65f637271e561b03680adaa6573015dfb106" +checksum = "abb745187d7f4d76267b37485a65e0149edd0e91a4cfcdd3f27524ad86cee9f3" dependencies = [ "clap", ] diff --git a/crates/bws/Cargo.toml b/crates/bws/Cargo.toml index 608db45a8..3766ad682 100644 --- a/crates/bws/Cargo.toml +++ b/crates/bws/Cargo.toml @@ -21,7 +21,7 @@ chrono = { version = "0.4.33", features = [ "std", ], default-features = false } clap = { version = "4.4.18", features = ["derive", "env", "string"] } -clap_complete = "4.4.9" +clap_complete = "4.4.10" color-eyre = "0.6" comfy-table = "^7.1.0" directories = "5.0.1" From 4883eba21061b67fa5b821aa8e6926d976eeb621 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 11:41:26 +0100 Subject: [PATCH 25/34] [deps]: Update Rust crate supports-color to v3 (#582) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [supports-color](https://togithub.com/zkat/supports-color) | dependencies | major | `2.1.0` -> `3.0.0` | --- ### Release Notes
zkat/supports-color (supports-color) ### [`v3.0.0`](https://togithub.com/zkat/supports-color/blob/HEAD/CHANGELOG.md#300-2024-02-04) [Compare Source](https://togithub.com/zkat/supports-color/compare/v2.1.0...v3.0.0) ##### Features - **deps:** Replace `is-terminal` with `std::io::IsTerminal` ([#​11](https://togithub.com/zkat/supports-color/issues/11)) ([6fb6e359](https://togithub.com/zkat/supports-color/commit/6fb6e35961055a701264d879744f615c25b7629d)) - **BREAKING CHANGE**: This bumps the MSRV to 1.70.0 due to the new `std::io::IsTerminal` API.
--- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/bitwarden/sdk). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Cargo.lock | 20 ++++---------------- crates/bitwarden-cli/Cargo.toml | 2 +- crates/bws/Cargo.toml | 2 +- 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6cae436a0..9054b7734 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1778,22 +1778,11 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" -[[package]] -name = "is-terminal" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" -dependencies = [ - "hermit-abi", - "rustix", - "windows-sys 0.52.0", -] - [[package]] name = "is_ci" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb" +checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" [[package]] name = "itertools" @@ -3215,11 +3204,10 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "supports-color" -version = "2.1.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6398cde53adc3c4557306a96ce67b302968513830a77a95b2b17305d9719a89" +checksum = "9829b314621dfc575df4e409e79f9d6a66a3bd707ab73f23cb4aa3a854ac854f" dependencies = [ - "is-terminal", "is_ci", ] diff --git a/crates/bitwarden-cli/Cargo.toml b/crates/bitwarden-cli/Cargo.toml index bded30904..4248c9189 100644 --- a/crates/bitwarden-cli/Cargo.toml +++ b/crates/bitwarden-cli/Cargo.toml @@ -8,4 +8,4 @@ rust-version = "1.57" clap = { version = "4.4.18", features = ["derive"] } color-eyre = "0.6" inquire = "0.6.2" -supports-color = "2.1.0" +supports-color = "3.0.0" diff --git a/crates/bws/Cargo.toml b/crates/bws/Cargo.toml index 3766ad682..8acc9f92c 100644 --- a/crates/bws/Cargo.toml +++ b/crates/bws/Cargo.toml @@ -34,7 +34,7 @@ regex = { version = "1.10.3", features = [ serde = "^1.0.196" serde_json = "^1.0.113" serde_yaml = "0.9" -supports-color = "2.1.0" +supports-color = "3.0.0" thiserror = "1.0.56" tokio = { version = "1.36.0", features = ["rt-multi-thread", "macros"] } toml = "0.8.9" From 851f4a6896580894b7e95a106d78fbe152879c9d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 11:41:46 +0100 Subject: [PATCH 26/34] [deps]: Update @types/node to v18.19.14 (#574) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [@types/node](https://togithub.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node) ([source](https://togithub.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node)) | [`18.19.10` -> `18.19.14`](https://renovatebot.com/diffs/npm/@types%2fnode/18.19.10/18.19.14) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@types%2fnode/18.19.14?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@types%2fnode/18.19.14?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@types%2fnode/18.19.10/18.19.14?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@types%2fnode/18.19.10/18.19.14?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/bitwarden/sdk). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- languages/js/sdk-client/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/languages/js/sdk-client/package-lock.json b/languages/js/sdk-client/package-lock.json index d1192e6f5..eb8d010db 100644 --- a/languages/js/sdk-client/package-lock.json +++ b/languages/js/sdk-client/package-lock.json @@ -39,9 +39,9 @@ } }, "node_modules/@types/node": { - "version": "18.19.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.10.tgz", - "integrity": "sha512-IZD8kAM02AW1HRDTPOlz3npFava678pr8Ie9Vp8uRhBROXAv8MXT2pCnGZZAKYdromsNQLHQcfWQ6EOatVLtqA==", + "version": "18.19.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.14.tgz", + "integrity": "sha512-EnQ4Us2rmOS64nHDWr0XqAD8DsO6f3XR6lf9UIIrZQpUzPVdN/oPuEzfDWNHSyXLvoGgjuEm/sPwFGSSs35Wtg==", "dev": true, "dependencies": { "undici-types": "~5.26.4" From a67b173f9acdc103a2274f85382b1a53dcfd5d02 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 13:43:17 +0100 Subject: [PATCH 27/34] [PM-6067] [deps]: Lock file maintenance (#584) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Update | Change | |---|---| | lockFileMaintenance | All locks refreshed | 🔧 This Pull Request updates lock files to use the latest dependency versions. --- ### Configuration 📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://togithub.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/bitwarden/sdk). [PM-6067](https://bitwarden.atlassian.net/browse/PM-6067) [PM-6067]: https://bitwarden.atlassian.net/browse/PM-6067?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Cargo.lock | 75 +++++++++++++++---------- crates/bitwarden-napi/package-lock.json | 6 +- package-lock.json | 6 +- 3 files changed, 51 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9054b7734..98274e54f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -78,9 +78,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "2faccea4cc4ab4a667ce676a30e8ec13922a692c99bb8f5b11f1502c72e04220" [[package]] name = "anstyle-parse" @@ -1242,9 +1242,9 @@ dependencies = [ [[package]] name = "eyre" -version = "0.6.11" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6267a1fa6f59179ea4afc8e50fd8612a3cc60bc858f786ff877a4a8cb042799" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" dependencies = [ "indenter", "once_cell", @@ -1502,7 +1502,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.2.1", + "indexmap 2.2.2", "slab", "tokio", "tokio-util", @@ -1661,9 +1661,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.59" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1717,9 +1717,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.1" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433de089bd45971eecf4668ee0ee8f4cec17db4f8bd8f7bc3197a6ce37aa7d9b" +checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -1848,9 +1848,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.152" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libloading" @@ -1949,9 +1949,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] @@ -1970,9 +1970,9 @@ dependencies = [ [[package]] name = "napi" -version = "2.15.0" +version = "2.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbf98e1bcb85cc441bbf7cdfb11070d2537a100e2697d75397b2584c32492d1" +checksum = "43792514b0c95c5beec42996da0c1b39265b02b75c97baa82d163d3ef55cbfa7" dependencies = [ "bitflags 2.4.2", "ctor", @@ -2082,6 +2082,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-integer" version = "0.1.45" @@ -2315,7 +2321,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5699cc8a63d1aa2b1ee8e12b9ad70ac790d65788cd36101fa37f87ea46c4cef" dependencies = [ "base64 0.21.7", - "indexmap 2.2.1", + "indexmap 2.2.2", "line-wrap", "quick-xml", "serde", @@ -2592,9 +2598,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.23" +version = "0.11.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" dependencies = [ "base64 0.21.7", "bytes", @@ -2619,6 +2625,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", "system-configuration", "tokio", "tokio-rustls", @@ -2688,9 +2695,9 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.30" +version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ "bitflags 2.4.2", "errno", @@ -3022,7 +3029,7 @@ version = "0.9.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adf8a49373e98a4c5f0ceb5d05aa7c648d75f63774981ed95b7c7443bbd50c6e" dependencies = [ - "indexmap 2.2.1", + "indexmap 2.2.2", "itoa", "ryu", "serde", @@ -3233,6 +3240,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "syntect" version = "5.1.0" @@ -3335,12 +3348,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.31" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" dependencies = [ "deranged", "itoa", + "num-conv", "powerfmt", "serde", "time-core", @@ -3355,10 +3369,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" dependencies = [ + "num-conv", "time-core", ] @@ -3465,7 +3480,7 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.2.1", + "indexmap 2.2.2", "serde", "serde_spanned", "toml_datetime", @@ -3920,9 +3935,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.3" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "weedle2" @@ -4107,9 +4122,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.35" +version = "0.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1931d78a9c73861da0134f453bb1f790ce49b2e30eba8410b4b79bac72b46a2d" +checksum = "a7cad8365489051ae9f054164e459304af2e7e9bb407c958076c8bf4aef52da5" dependencies = [ "memchr", ] diff --git a/crates/bitwarden-napi/package-lock.json b/crates/bitwarden-napi/package-lock.json index a8995bc77..9c9d87de9 100644 --- a/crates/bitwarden-napi/package-lock.json +++ b/crates/bitwarden-napi/package-lock.json @@ -95,9 +95,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.11.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.10.tgz", - "integrity": "sha512-rZEfe/hJSGYmdfX9tvcPMYeYPW2sNl50nsw4jZmRcaG0HIAb0WYEpsB05GOb53vjqpyE9GUhlDQ4jLSoB5q9kg==", + "version": "20.11.16", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.16.tgz", + "integrity": "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ==", "dev": true, "peer": true, "dependencies": { diff --git a/package-lock.json b/package-lock.json index 747d7d809..4ad23e90b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -346,9 +346,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.11.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.10.tgz", - "integrity": "sha512-rZEfe/hJSGYmdfX9tvcPMYeYPW2sNl50nsw4jZmRcaG0HIAb0WYEpsB05GOb53vjqpyE9GUhlDQ4jLSoB5q9kg==", + "version": "20.11.16", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.16.tgz", + "integrity": "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ==", "dev": true, "peer": true, "dependencies": { From 43bdcf0a51696c4d8aa0e337d31214531db8bafe Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 14:06:55 +0100 Subject: [PATCH 28/34] [deps]: Update prettier to v3.2.5 (#583) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [prettier](https://prettier.io) ([source](https://togithub.com/prettier/prettier)) | [`3.2.4` -> `3.2.5`](https://renovatebot.com/diffs/npm/prettier/3.2.4/3.2.5) | [![age](https://developer.mend.io/api/mc/badges/age/npm/prettier/3.2.5?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/prettier/3.2.5?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/prettier/3.2.4/3.2.5?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/prettier/3.2.4/3.2.5?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
prettier/prettier (prettier) ### [`v3.2.5`](https://togithub.com/prettier/prettier/blob/HEAD/CHANGELOG.md#325) [Compare Source](https://togithub.com/prettier/prettier/compare/3.2.4...3.2.5) [diff](https://togithub.com/prettier/prettier/compare/3.2.4...3.2.5) ##### Support Angular inline styles as single template literal ([#​15968](https://togithub.com/prettier/prettier/pull/15968) by [@​sosukesuzuki](https://togithub.com/sosukesuzuki)) [Angular v17](https://blog.angular.io/introducing-angular-v17-4d7033312e4b) supports single string inline styles. ```ts // Input @​Component({ template: `
...
`, styles: `h1 { color: blue; }`, }) export class AppComponent {} // Prettier 3.2.4 @​Component({ template: `
...
`, styles: `h1 { color: blue; }`, }) export class AppComponent {} // Prettier 3.2.5 @​Component({ template: `
...
`, styles: ` h1 { color: blue; } `, }) export class AppComponent {} ``` ##### Unexpected embedded formatting for Angular template ([#​15969](https://togithub.com/prettier/prettier/pull/15969) by [@​JounQin](https://togithub.com/JounQin)) Computed template should not be considered as Angular component template ```ts // Input const template = "foobar"; @​Component({ [template]: `

{{ hello }}

`, }) export class AppComponent {} // Prettier 3.2.4 const template = "foobar"; @​Component({ [template]: `

{{ hello }}

`, }) export class AppComponent {} // Prettier 3.2.5 const template = "foobar"; @​Component({ [template]: `

{{ hello }}

`, }) export class AppComponent {} ``` ##### Use `"json"` parser for `tsconfig.json` by default ([#​16012](https://togithub.com/prettier/prettier/pull/16012) by [@​sosukesuzuki](https://togithub.com/sosukesuzuki)) In [v2.3.0](https://prettier.io/blog/2024/01/12/3.2.0#new-jsonc-parser-added-15831httpsgithubcomprettierprettierpull15831-by-fiskerhttpsgithubcomfisker), we introduced `"jsonc"` parser which adds trialing comma **by default**. When adding a new parser we also define how it will be used based on the [`linguist-languages`](https://www.npmjs.com/package/linguist-languages) data. `tsconfig.json` is a special file used by [TypeScript](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html#using-tsconfigjson-or-jsconfigjson), it uses `.json` file extension, but it actually uses the [JSON with Comments](https://code.visualstudio.com/docs/languages/json#\_json-with-comments) syntax. However, we found that there are many third-party tools not recognize it correctly because of the confusing `.json` file extension. We decide to treat it as a JSON file for now to avoid the extra configuration step. To keep using the `"jsonc"` parser for your `tsconfig.json` files, add the following to your `.pretterrc` file ```json { "overrides": [ { "files": ["tsconfig.json", "jsconfig.json"], "options": { "parser": "jsonc" } } ] } ``` ``` ```
--- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/bitwarden/sdk). --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Hinton --- crates/bitwarden-napi/tsconfig.json | 4 ++-- package-lock.json | 8 ++++---- package.json | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/bitwarden-napi/tsconfig.json b/crates/bitwarden-napi/tsconfig.json index 8ec7e00d4..f977e0759 100644 --- a/crates/bitwarden-napi/tsconfig.json +++ b/crates/bitwarden-napi/tsconfig.json @@ -7,7 +7,7 @@ "strict": true, "noImplicitAny": true, "esModuleInterop": true, - "declaration": true, + "declaration": true }, - "include": ["src-ts", "src-ts/bitwarden_client", "src-ts/index.ts"], + "include": ["src-ts", "src-ts/bitwarden_client", "src-ts/index.ts"] } diff --git a/package-lock.json b/package-lock.json index 4ad23e90b..bdb82563e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "devDependencies": { "@openapitools/openapi-generator-cli": "2.9.0", "handlebars": "^4.7.8", - "prettier": "3.2.4", + "prettier": "3.2.5", "quicktype-core": "23.0.81", "rimraf": "5.0.5", "ts-node": "10.9.2", @@ -1471,9 +1471,9 @@ } }, "node_modules/prettier": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.4.tgz", - "integrity": "sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" diff --git a/package.json b/package.json index 14b5692a0..05195270e 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "devDependencies": { "@openapitools/openapi-generator-cli": "2.9.0", "handlebars": "^4.7.8", - "prettier": "3.2.4", + "prettier": "3.2.5", "quicktype-core": "23.0.81", "rimraf": "5.0.5", "ts-node": "10.9.2", From 11e36557dcb86c7cb376d278124371fb6e764b7d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 09:02:30 -0500 Subject: [PATCH 29/34] [deps]: Update codecov/codecov-action action to v4 (#580) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/rust-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust-test.yml b/.github/workflows/rust-test.yml index 93a2ddecf..8408dd1d7 100644 --- a/.github/workflows/rust-test.yml +++ b/.github/workflows/rust-test.yml @@ -73,7 +73,7 @@ jobs: run: cargo llvm-cov --all-features --lcov --output-path lcov.info --ignore-filename-regex "crates/bitwarden-api-" - name: Upload to codecov.io - uses: codecov/codecov-action@4fe8c5f003fae66aa5ebb77cfd3e7bfbbda0b6b0 # v3.1.5 + uses: codecov/codecov-action@e0b68c6749509c5f83f984dd99a76a1c1a231044 # v4.0.1 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} From 46c72f3dbadc3dce09acb1775f7033b2a3ba882a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 09:13:05 -0500 Subject: [PATCH 30/34] [deps]: Update ruby/setup-ruby action to v1.171.0 (#578) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/publish-ruby.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-ruby.yml b/.github/workflows/publish-ruby.yml index 81aedcc22..12abd18f0 100644 --- a/.github/workflows/publish-ruby.yml +++ b/.github/workflows/publish-ruby.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Set up Ruby - uses: ruby/setup-ruby@bd03e04863f52d169e18a2b190e8fa6b84938215 # v1.170.0 + uses: ruby/setup-ruby@22fdc77bf4148f810455b226c90fb81b5cbc00a7 # v1.171.0 with: ruby-version: 3.2 From 69a5d7bf01b17aeccb3be7bc77272bf50cb24de9 Mon Sep 17 00:00:00 2001 From: Vince Grassia <593223+vgrassia@users.noreply.github.com> Date: Mon, 5 Feb 2024 10:46:47 -0500 Subject: [PATCH 31/34] Update Gradle action as recommended in new release (#585) --- .github/workflows/build-android.yml | 2 +- .github/workflows/build-java.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-android.yml b/.github/workflows/build-android.yml index e1aeea244..efa8c7bf0 100644 --- a/.github/workflows/build-android.yml +++ b/.github/workflows/build-android.yml @@ -102,7 +102,7 @@ jobs: run: ./build-schemas.sh - name: Publish - uses: gradle/gradle-build-action@a8f75513eafdebd8141bd1cd4e30fcd194af8dfa # v2.12.0 + uses: gradle/actions/setup-gradle@ec92e829475ac0c2315ea8f9eced72db85bb337a # v3.0.0 with: arguments: sdk:publish build-root-directory: languages/kotlin diff --git a/.github/workflows/build-java.yml b/.github/workflows/build-java.yml index 0ed8997ca..2559f659c 100644 --- a/.github/workflows/build-java.yml +++ b/.github/workflows/build-java.yml @@ -61,7 +61,7 @@ jobs: path: languages/java/src/main/resources/win32-x86-64 - name: Publish Maven - uses: gradle/gradle-build-action@a8f75513eafdebd8141bd1cd4e30fcd194af8dfa # v2.12.0 + uses: gradle/actions/setup-gradle@ec92e829475ac0c2315ea8f9eced72db85bb337a # v3.0.0 with: arguments: publish build-root-directory: languages/java From b98a019482d06e8c89afeceded1b6bffc32bd127 Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Tue, 6 Feb 2024 10:34:43 +0100 Subject: [PATCH 32/34] Use base renovate config (#586) Use shared renovate config. --- .github/renovate.json | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/.github/renovate.json b/.github/renovate.json index ae4ad0e9d..ddfc900dc 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -1,21 +1,8 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:base", - "github>bitwarden/renovate-config:pin-actions", - ":combinePatchMinorReleases", - ":dependencyDashboard", - ":maintainLockFilesWeekly", - ":prConcurrentLimit10", - ":rebaseStalePrs", - ":separateMajorReleases", - "group:monorepos", - "schedule:weekends" - ], + "extends": ["github>bitwarden/renovate-config:non-pinned"], "separateMajorMinor": true, "enabledManagers": ["cargo", "github-actions", "npm", "nuget"], - "commitMessagePrefix": "[deps]:", - "commitMessageTopic": "{{depName}}", "packageRules": [ { "matchManagers": ["cargo"], From 714c2d40d1811c13c751b6de9a9fe0ae51f61583 Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Tue, 6 Feb 2024 11:52:38 +0100 Subject: [PATCH 33/34] [PM-3438] Vault exports (#561) Vault export implementation. New crate `bitwarden-exporters` which defines it's own data structure, isolating it from the other code areas. It primarily focuses on converting a data model to csv and json. Currently all data has to be decrypted independently of what is required for the export which is an area of improvement for the future. --- .prettierignore | 3 + .vscode/settings.json | 2 + Cargo.lock | 34 + crates/bitwarden-exporters/Cargo.toml | 26 + crates/bitwarden-exporters/README.md | 6 + .../resources/json_export.json | 146 ++++ crates/bitwarden-exporters/src/csv.rs | 266 ++++++ crates/bitwarden-exporters/src/json.rs | 762 ++++++++++++++++++ crates/bitwarden-exporters/src/lib.rs | 142 ++++ crates/bitwarden/Cargo.toml | 1 + crates/bitwarden/src/error.rs | 5 + .../src/tool/exporters/client_exporter.rs | 6 +- crates/bitwarden/src/tool/exporters/mod.rs | 278 ++++++- crates/bitwarden/src/vault/cipher/field.rs | 8 +- .../bitwarden/src/vault/cipher/linked_id.rs | 8 +- crates/bitwarden/src/vault/cipher/mod.rs | 7 +- .../bitwarden/src/vault/cipher/secure_note.rs | 2 +- crates/bitwarden/src/vault/folder.rs | 6 +- crates/bitwarden/src/vault/mod.rs | 7 +- 19 files changed, 1685 insertions(+), 30 deletions(-) create mode 100644 crates/bitwarden-exporters/Cargo.toml create mode 100644 crates/bitwarden-exporters/README.md create mode 100644 crates/bitwarden-exporters/resources/json_export.json create mode 100644 crates/bitwarden-exporters/src/csv.rs create mode 100644 crates/bitwarden-exporters/src/json.rs create mode 100644 crates/bitwarden-exporters/src/lib.rs diff --git a/.prettierignore b/.prettierignore index d5ffe5a0e..97474cca9 100644 --- a/.prettierignore +++ b/.prettierignore @@ -7,3 +7,6 @@ schemas /crates/bitwarden-napi/src-ts/bitwarden_client/schemas.ts about.hbs support/docs/template.hbs + +# Test fixtures +crates/bitwarden-exporters/resources/* diff --git a/.vscode/settings.json b/.vscode/settings.json index 63c3dccaa..e92fcfb76 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,8 +15,10 @@ "Pbkdf", "PKCS8", "repr", + "reprompt", "reqwest", "schemars", + "totp", "uniffi", "wordlist", "zxcvbn" diff --git a/Cargo.lock b/Cargo.lock index 98274e54f..b2728124b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -352,6 +352,7 @@ dependencies = [ "bitwarden-api-api", "bitwarden-api-identity", "bitwarden-crypto", + "bitwarden-exporters", "bitwarden-generators", "chrono", "getrandom 0.2.12", @@ -451,6 +452,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "bitwarden-exporters" +version = "0.1.0" +dependencies = [ + "chrono", + "csv", + "serde", + "serde_json", + "thiserror", + "uuid", +] + [[package]] name = "bitwarden-generators" version = "0.1.0" @@ -999,6 +1012,27 @@ dependencies = [ "typenum", ] +[[package]] +name = "csv" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +dependencies = [ + "memchr", +] + [[package]] name = "ctor" version = "0.2.6" diff --git a/crates/bitwarden-exporters/Cargo.toml b/crates/bitwarden-exporters/Cargo.toml new file mode 100644 index 000000000..0008fbb49 --- /dev/null +++ b/crates/bitwarden-exporters/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "bitwarden-exporters" +version = "0.1.0" +authors = ["Bitwarden Inc"] +license-file = "LICENSE" +repository = "https://github.com/bitwarden/sdk" +homepage = "https://bitwarden.com" +description = """ +Internal crate for the bitwarden crate. Do not use. +""" +keywords = ["bitwarden"] +edition = "2021" +rust-version = "1.57" +exclude = ["/resources"] + +[dependencies] +chrono = { version = ">=0.4.26, <0.5", features = [ + "clock", + "serde", + "std", +], default-features = false } +csv = "1.3.0" +serde = { version = ">=1.0, <2.0", features = ["derive"] } +serde_json = ">=1.0.96, <2.0" +thiserror = ">=1.0.40, <2.0" +uuid = { version = ">=1.3.3, <2.0", features = ["serde"] } diff --git a/crates/bitwarden-exporters/README.md b/crates/bitwarden-exporters/README.md new file mode 100644 index 000000000..59936680f --- /dev/null +++ b/crates/bitwarden-exporters/README.md @@ -0,0 +1,6 @@ +# Bitwarden Exporters + +This is an internal crate for the Bitwarden SDK do not depend on this directly and use the +[`bitwarden`](https://crates.io/crates/bitwarden) crate instead. + +This crate does not follow semantic versioning and the public interface may change at any time. diff --git a/crates/bitwarden-exporters/resources/json_export.json b/crates/bitwarden-exporters/resources/json_export.json new file mode 100644 index 000000000..ad5380550 --- /dev/null +++ b/crates/bitwarden-exporters/resources/json_export.json @@ -0,0 +1,146 @@ +{ + "encrypted": false, + "folders": [ + { + "id": "942e2984-1b9a-453b-b039-b107012713b9", + "name": "Important" + } + ], + "items": [ + { + "id": "25c8c414-b446-48e9-a1bd-b10700bbd740", + "folderId": "942e2984-1b9a-453b-b039-b107012713b9", + "organizationId": null, + "collectionIds": null, + "name": "Bitwarden", + "notes": "My note", + "type": 1, + "login": { + "username": "test@bitwarden.com", + "password": "asdfasdfasdf", + "uris": [ + { + "uri": "https://vault.bitwarden.com", + "match": null + } + ], + "totp": "ABC", + "fido2Credentials": [] + }, + "favorite": true, + "reprompt": 0, + "fields": [ + { + "name": "Text", + "value": "A", + "type": 0, + "linkedId": null + }, + { + "name": "Hidden", + "value": "B", + "type": 1, + "linkedId": null + }, + { + "name": "Boolean (true)", + "value": "true", + "type": 2, + "linkedId": null + }, + { + "name": "Boolean (false)", + "value": "false", + "type": 2, + "linkedId": null + }, + { + "name": "Linked", + "value": null, + "type": 3, + "linkedId": 101 + } + ], + "passwordHistory": null, + "revisionDate": "2024-01-30T14:09:33.753Z", + "creationDate": "2024-01-30T11:23:54.416Z", + "deletedDate": null + }, + { + "id": "23f0f877-42b1-4820-a850-b10700bc41eb", + "folderId": null, + "organizationId": null, + "collectionIds": null, + "name": "My secure note", + "notes": "Very secure!", + "type": 2, + "secureNote": { + "type": 0 + }, + "favorite": false, + "reprompt": 0, + "passwordHistory": null, + "revisionDate": "2024-01-30T11:25:25.466Z", + "creationDate": "2024-01-30T11:25:25.466Z", + "deletedDate": null + }, + { + "id": "3ed8de45-48ee-4e26-a2dc-b10701276c53", + "folderId": null, + "organizationId": null, + "collectionIds": null, + "name": "My card", + "notes": null, + "type": 3, + "card": { + "cardholderName": "John Doe", + "expMonth": "1", + "expYear": "2032", + "code": "123", + "brand": "Visa", + "number": "4111111111111111" + }, + "favorite": false, + "reprompt": 0, + "passwordHistory": null, + "revisionDate": "2024-01-30T17:55:36.150Z", + "creationDate": "2024-01-30T17:55:36.150Z", + "deletedDate": null + }, + { + "id": "41cc3bc1-c3d9-4637-876c-b10701273712", + "folderId": "942e2984-1b9a-453b-b039-b107012713b9", + "organizationId": null, + "collectionIds": null, + "name": "My identity", + "notes": null, + "type": 4, + "identity": { + "title": "Mr", + "firstName": "John", + "middleName": null, + "lastName": "Doe", + "address1": null, + "address2": null, + "address3": null, + "city": null, + "state": null, + "postalCode": null, + "country": null, + "company": "Bitwarden", + "email": null, + "phone": null, + "ssn": null, + "username": "JDoe", + "passportNumber": null, + "licenseNumber": null + }, + "favorite": false, + "reprompt": 0, + "passwordHistory": null, + "revisionDate": "2024-01-30T17:54:50.706Z", + "creationDate": "2024-01-30T17:54:50.706Z", + "deletedDate": null + } + ] +} \ No newline at end of file diff --git a/crates/bitwarden-exporters/src/csv.rs b/crates/bitwarden-exporters/src/csv.rs new file mode 100644 index 000000000..644eeb030 --- /dev/null +++ b/crates/bitwarden-exporters/src/csv.rs @@ -0,0 +1,266 @@ +use std::collections::HashMap; + +use csv::Writer; +use serde::Serializer; +use thiserror::Error; +use uuid::Uuid; + +use crate::{Cipher, CipherType, Field, Folder}; + +#[derive(Debug, Error)] +pub enum CsvError { + #[error("CSV error")] + Csv, +} + +pub(crate) fn export_csv(folders: Vec, ciphers: Vec) -> Result { + let folders: HashMap = folders.into_iter().map(|f| (f.id, f.name)).collect(); + + let rows = ciphers + .into_iter() + .filter(|c| matches!(c.r#type, CipherType::Login(_) | CipherType::SecureNote(_))) + .map(|c| { + let login = if let CipherType::Login(l) = &c.r#type { + Some(l) + } else { + None + }; + + CsvRow { + folder: c + .folder_id + .and_then(|f| folders.get(&f)) + .map(|f| f.to_owned()), + favorite: c.favorite, + r#type: c.r#type.to_string(), + name: c.name.to_owned(), + notes: c.notes.to_owned(), + fields: c.fields, + reprompt: c.reprompt, + login_uri: login + .map(|l| l.login_uris.iter().flat_map(|l| l.uri.clone()).collect()) + .unwrap_or_default(), + login_username: login.and_then(|l| l.username.clone()), + login_password: login.and_then(|l| l.password.clone()), + login_totp: login.and_then(|l| l.totp.clone()), + } + }); + + let mut wtr = Writer::from_writer(vec![]); + for row in rows { + wtr.serialize(row).unwrap(); + } + + String::from_utf8(wtr.into_inner().map_err(|_| CsvError::Csv)?).map_err(|_| CsvError::Csv) +} + +/// CSV export format. See https://bitwarden.com/help/condition-bitwarden-import/#condition-a-csv +/// +/// Be careful when changing this struct to maintain compatibility with old exports. +#[derive(serde::Serialize)] +struct CsvRow { + folder: Option, + #[serde(serialize_with = "bool_serialize")] + favorite: bool, + r#type: String, + name: String, + notes: Option, + #[serde(serialize_with = "fields_serialize")] + fields: Vec, + reprompt: u8, + #[serde(serialize_with = "vec_serialize")] + login_uri: Vec, + login_username: Option, + login_password: Option, + login_totp: Option, +} + +fn vec_serialize(x: &[String], s: S) -> Result +where + S: Serializer, +{ + s.serialize_str(x.join(",").as_str()) +} + +fn bool_serialize(x: &bool, s: S) -> Result +where + S: Serializer, +{ + s.serialize_str(if *x { "1" } else { "" }) +} + +fn fields_serialize(x: &[Field], s: S) -> Result +where + S: Serializer, +{ + s.serialize_str( + x.iter() + .map(|f| { + format!( + "{}: {}", + f.name.to_owned().unwrap_or_default(), + f.value.to_owned().unwrap_or_default() + ) + }) + .collect::>() + .join("\n") + .as_str(), + ) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{Card, Identity, Login, LoginUri}; + + #[test] + fn test_export_csv() { + let folders = vec![ + Folder { + id: "d55d65d7-c161-40a4-94ca-b0d20184d91a".parse().unwrap(), + name: "Test Folder A".to_string(), + }, + Folder { + id: "583e7665-0126-4d37-9139-b0d20184dd86".parse().unwrap(), + name: "Test Folder B".to_string(), + }, + ]; + let ciphers = vec![ + Cipher { + id: "d55d65d7-c161-40a4-94ca-b0d20184d91a".parse().unwrap(), + folder_id: None, + name: "test@bitwarden.com".to_string(), + notes: None, + r#type: CipherType::Login(Box::new(Login { + username: Some("test@bitwarden.com".to_string()), + password: Some("Abc123".to_string()), + login_uris: vec![LoginUri { + uri: Some("https://google.com".to_string()), + r#match: None, + }], + totp: None, + })), + favorite: false, + reprompt: 0, + fields: vec![], + revision_date: "2024-01-30T11:28:20.036Z".parse().unwrap(), + creation_date: "2024-01-30T11:28:20.036Z".parse().unwrap(), + deleted_date: None, + }, + Cipher { + id: "7dd81bd0-cc72-4f42-96e7-b0fc014e71a3".parse().unwrap(), + folder_id: Some("583e7665-0126-4d37-9139-b0d20184dd86".parse().unwrap()), + name: "Steam Account".to_string(), + notes: None, + r#type: CipherType::Login(Box::new(Login { + username: Some("steam".to_string()), + password: Some("3Pvb8u7EfbV*nJ".to_string()), + login_uris: vec![LoginUri { + uri: Some("https://steampowered.com".to_string()), + r#match: None, + }], + totp: Some("steam://ABCD123".to_string()), + })), + favorite: true, + reprompt: 0, + fields: vec![ + Field { + name: Some("Test".to_string()), + value: Some("v".to_string()), + r#type: 0, + linked_id: None, + }, + Field { + name: Some("Hidden".to_string()), + value: Some("asdfer".to_string()), + r#type: 1, + linked_id: None, + }, + ], + revision_date: "2024-01-30T11:28:20.036Z".parse().unwrap(), + creation_date: "2024-01-30T11:28:20.036Z".parse().unwrap(), + deleted_date: None, + }, + ]; + + let csv = export_csv(folders, ciphers).unwrap(); + let expected = [ + "folder,favorite,type,name,notes,fields,reprompt,login_uri,login_username,login_password,login_totp", + ",,login,test@bitwarden.com,,,0,https://google.com,test@bitwarden.com,Abc123,", + "Test Folder B,1,login,Steam Account,,\"Test: v\nHidden: asdfer\",0,https://steampowered.com,steam,3Pvb8u7EfbV*nJ,steam://ABCD123", + "", + ].join("\n"); + + assert_eq!(csv, expected); + } + + #[test] + fn test_export_ignore_card() { + let folders = vec![]; + let ciphers = vec![Cipher { + id: "d55d65d7-c161-40a4-94ca-b0d20184d91a".parse().unwrap(), + folder_id: None, + name: "My Card".to_string(), + notes: None, + r#type: CipherType::Card(Box::new(Card { + cardholder_name: None, + exp_month: None, + exp_year: None, + code: None, + brand: None, + number: None, + })), + favorite: false, + reprompt: 0, + fields: vec![], + revision_date: "2024-01-30T11:28:20.036Z".parse().unwrap(), + creation_date: "2024-01-30T11:28:20.036Z".parse().unwrap(), + deleted_date: None, + }]; + + let csv = export_csv(folders, ciphers).unwrap(); + + assert_eq!(csv, ""); + } + + #[test] + fn test_export_ignore_identity() { + let folders = vec![]; + let ciphers = vec![Cipher { + id: "d55d65d7-c161-40a4-94ca-b0d20184d91a".parse().unwrap(), + folder_id: None, + name: "My Identity".to_string(), + notes: None, + r#type: CipherType::Identity(Box::new(Identity { + title: None, + first_name: None, + middle_name: None, + last_name: None, + address1: None, + address2: None, + address3: None, + city: None, + state: None, + postal_code: None, + country: None, + company: None, + email: None, + phone: None, + ssn: None, + username: None, + passport_number: None, + license_number: None, + })), + favorite: false, + reprompt: 0, + fields: vec![], + revision_date: "2024-01-30T11:28:20.036Z".parse().unwrap(), + creation_date: "2024-01-30T11:28:20.036Z".parse().unwrap(), + deleted_date: None, + }]; + + let csv = export_csv(folders, ciphers).unwrap(); + + assert_eq!(csv, ""); + } +} diff --git a/crates/bitwarden-exporters/src/json.rs b/crates/bitwarden-exporters/src/json.rs new file mode 100644 index 000000000..3f6c72c1f --- /dev/null +++ b/crates/bitwarden-exporters/src/json.rs @@ -0,0 +1,762 @@ +use chrono::{DateTime, Utc}; +use thiserror::Error; +use uuid::Uuid; + +use crate::{Card, Cipher, CipherType, Field, Folder, Identity, Login, LoginUri, SecureNote}; + +#[derive(Error, Debug)] +pub enum JsonError { + #[error("JSON error: {0}")] + Serde(#[from] serde_json::Error), +} + +pub(crate) fn export_json(folders: Vec, ciphers: Vec) -> Result { + let export = JsonExport { + encrypted: false, + folders: folders.into_iter().map(|f| f.into()).collect(), + items: ciphers.into_iter().map(|c| c.into()).collect(), + }; + + Ok(serde_json::to_string_pretty(&export)?) +} + +/// JSON export format. These are intentionally decoupled from the internal data structures to +/// ensure internal changes are not reflected in the public exports. +/// +/// Be careful about changing these structs to maintain compatibility with old exporters/importers. +#[derive(serde::Serialize)] +#[serde(rename_all = "camelCase")] +struct JsonExport { + encrypted: bool, + folders: Vec, + items: Vec, +} + +#[derive(serde::Serialize)] +#[serde(rename_all = "camelCase")] +struct JsonFolder { + id: Uuid, + name: String, +} + +impl From for JsonFolder { + fn from(folder: Folder) -> Self { + JsonFolder { + id: folder.id, + name: folder.name, + } + } +} + +#[derive(serde::Serialize)] +#[serde(rename_all = "camelCase")] +struct JsonCipher { + id: Uuid, + folder_id: Option, + // Organizational IDs which are always empty in personal exports + organization_id: Option, + collection_ids: Option>, + + name: String, + notes: Option, + + r#type: u8, + #[serde(skip_serializing_if = "Option::is_none")] + login: Option, + #[serde(skip_serializing_if = "Option::is_none")] + identity: Option, + #[serde(skip_serializing_if = "Option::is_none")] + card: Option, + #[serde(skip_serializing_if = "Option::is_none")] + secure_note: Option, + + favorite: bool, + reprompt: u8, + + #[serde(skip_serializing_if = "Vec::is_empty")] + fields: Vec, + password_history: Option>, + + revision_date: DateTime, + creation_date: DateTime, + deleted_date: Option>, +} + +#[derive(serde::Serialize)] +#[serde(rename_all = "camelCase")] +struct JsonLogin { + username: Option, + password: Option, + uris: Vec, + totp: Option, + fido2_credentials: Vec, +} + +impl From for JsonLogin { + fn from(login: Login) -> Self { + JsonLogin { + username: login.username, + password: login.password, + uris: login.login_uris.into_iter().map(|u| u.into()).collect(), + totp: login.totp, + fido2_credentials: vec![], + } + } +} + +#[derive(serde::Serialize)] +#[serde(rename_all = "camelCase")] +struct JsonLoginUri { + uri: Option, + r#match: Option, +} + +impl From for JsonLoginUri { + fn from(login_uri: LoginUri) -> Self { + JsonLoginUri { + uri: login_uri.uri, + r#match: login_uri.r#match, + } + } +} + +#[derive(serde::Serialize)] +#[serde(rename_all = "camelCase")] +struct JsonSecureNote { + r#type: u8, +} + +impl From for JsonSecureNote { + fn from(note: SecureNote) -> Self { + JsonSecureNote { + r#type: note.r#type as u8, + } + } +} + +#[derive(serde::Serialize)] +#[serde(rename_all = "camelCase")] +struct JsonCard { + cardholder_name: Option, + exp_month: Option, + exp_year: Option, + code: Option, + brand: Option, + number: Option, +} + +impl From for JsonCard { + fn from(card: Card) -> Self { + JsonCard { + cardholder_name: card.cardholder_name, + exp_month: card.exp_month, + exp_year: card.exp_year, + code: card.code, + brand: card.brand, + number: card.number, + } + } +} + +#[derive(serde::Serialize)] +#[serde(rename_all = "camelCase")] +struct JsonIdentity { + title: Option, + first_name: Option, + middle_name: Option, + last_name: Option, + address1: Option, + address2: Option, + address3: Option, + city: Option, + state: Option, + postal_code: Option, + country: Option, + company: Option, + email: Option, + phone: Option, + ssn: Option, + username: Option, + passport_number: Option, + license_number: Option, +} + +impl From for JsonIdentity { + fn from(identity: Identity) -> Self { + JsonIdentity { + title: identity.title, + first_name: identity.first_name, + middle_name: identity.middle_name, + last_name: identity.last_name, + address1: identity.address1, + address2: identity.address2, + address3: identity.address3, + city: identity.city, + state: identity.state, + postal_code: identity.postal_code, + country: identity.country, + company: identity.company, + email: identity.email, + phone: identity.phone, + ssn: identity.ssn, + username: identity.username, + passport_number: identity.passport_number, + license_number: identity.license_number, + } + } +} + +#[derive(serde::Serialize)] +#[serde(rename_all = "camelCase")] +struct JsonField { + name: Option, + value: Option, + r#type: u8, + linked_id: Option, +} + +impl From for JsonField { + fn from(field: Field) -> Self { + JsonField { + name: field.name, + value: field.value, + r#type: field.r#type, + linked_id: field.linked_id, + } + } +} + +impl From for JsonCipher { + fn from(cipher: Cipher) -> Self { + let r#type = match cipher.r#type { + CipherType::Login(_) => 1, + CipherType::SecureNote(_) => 2, + CipherType::Card(_) => 3, + CipherType::Identity(_) => 4, + }; + + let (login, secure_note, card, identity) = match cipher.r#type { + CipherType::Login(l) => (Some((*l).into()), None, None, None), + CipherType::SecureNote(s) => (None, Some((*s).into()), None, None), + CipherType::Card(c) => (None, None, Some((*c).into()), None), + CipherType::Identity(i) => (None, None, None, Some((*i).into())), + }; + + JsonCipher { + id: cipher.id, + folder_id: cipher.folder_id, + organization_id: None, + collection_ids: None, + name: cipher.name, + notes: cipher.notes, + r#type, + login, + identity, + card, + secure_note, + favorite: cipher.favorite, + reprompt: cipher.reprompt, + fields: cipher.fields.into_iter().map(|f| f.into()).collect(), + password_history: None, + revision_date: cipher.revision_date, + creation_date: cipher.creation_date, + deleted_date: cipher.deleted_date, + } + } +} + +#[cfg(test)] +mod tests { + use std::{fs, io::Read, path::PathBuf}; + + use super::*; + use crate::{Cipher, Field, LoginUri, SecureNoteType}; + + #[test] + fn test_convert_login() { + let cipher = Cipher { + id: "25c8c414-b446-48e9-a1bd-b10700bbd740".parse().unwrap(), + folder_id: Some("942e2984-1b9a-453b-b039-b107012713b9".parse().unwrap()), + + name: "Bitwarden".to_string(), + notes: Some("My note".to_string()), + + r#type: CipherType::Login(Box::new(Login { + username: Some("test@bitwarden.com".to_string()), + password: Some("asdfasdfasdf".to_string()), + login_uris: vec![LoginUri { + uri: Some("https://vault.bitwarden.com".to_string()), + r#match: None, + }], + totp: Some("ABC".to_string()), + })), + + favorite: true, + reprompt: 0, + + fields: vec![ + Field { + name: Some("Text".to_string()), + value: Some("A".to_string()), + r#type: 0, + linked_id: None, + }, + Field { + name: Some("Hidden".to_string()), + value: Some("B".to_string()), + r#type: 1, + linked_id: None, + }, + Field { + name: Some("Boolean (true)".to_string()), + value: Some("true".to_string()), + r#type: 2, + linked_id: None, + }, + Field { + name: Some("Boolean (false)".to_string()), + value: Some("false".to_string()), + r#type: 2, + linked_id: None, + }, + Field { + name: Some("Linked".to_string()), + value: None, + r#type: 3, + linked_id: Some(101), + }, + ], + + revision_date: "2024-01-30T14:09:33.753Z".parse().unwrap(), + creation_date: "2024-01-30T11:23:54.416Z".parse().unwrap(), + deleted_date: None, + }; + + let json = serde_json::to_string(&JsonCipher::from(cipher)).unwrap(); + + let expected = r#"{ + "passwordHistory": null, + "revisionDate": "2024-01-30T14:09:33.753Z", + "creationDate": "2024-01-30T11:23:54.416Z", + "deletedDate": null, + "id": "25c8c414-b446-48e9-a1bd-b10700bbd740", + "organizationId": null, + "folderId": "942e2984-1b9a-453b-b039-b107012713b9", + "type": 1, + "reprompt": 0, + "name": "Bitwarden", + "notes": "My note", + "favorite": true, + "fields": [ + { + "name": "Text", + "value": "A", + "type": 0, + "linkedId": null + }, + { + "name": "Hidden", + "value": "B", + "type": 1, + "linkedId": null + }, + { + "name": "Boolean (true)", + "value": "true", + "type": 2, + "linkedId": null + }, + { + "name": "Boolean (false)", + "value": "false", + "type": 2, + "linkedId": null + }, + { + "name": "Linked", + "value": null, + "type": 3, + "linkedId": 101 + } + ], + "login": { + "fido2Credentials": [], + "uris": [ + { + "match": null, + "uri": "https://vault.bitwarden.com" + } + ], + "username": "test@bitwarden.com", + "password": "asdfasdfasdf", + "totp": "ABC" + }, + "collectionIds": null + }"#; + + assert_eq!( + json.parse::().unwrap(), + expected.parse::().unwrap() + ) + } + + #[test] + fn test_convert_secure_note() { + let cipher = Cipher { + id: "23f0f877-42b1-4820-a850-b10700bc41eb".parse().unwrap(), + folder_id: None, + + name: "My secure note".to_string(), + notes: Some("Very secure!".to_string()), + + r#type: CipherType::SecureNote(Box::new(SecureNote { + r#type: SecureNoteType::Generic, + })), + + favorite: false, + reprompt: 0, + + fields: vec![], + + revision_date: "2024-01-30T11:25:25.466Z".parse().unwrap(), + creation_date: "2024-01-30T11:25:25.466Z".parse().unwrap(), + deleted_date: None, + }; + + let json = serde_json::to_string(&JsonCipher::from(cipher)).unwrap(); + + let expected = r#"{ + "passwordHistory": null, + "revisionDate": "2024-01-30T11:25:25.466Z", + "creationDate": "2024-01-30T11:25:25.466Z", + "deletedDate": null, + "id": "23f0f877-42b1-4820-a850-b10700bc41eb", + "organizationId": null, + "folderId": null, + "type": 2, + "reprompt": 0, + "name": "My secure note", + "notes": "Very secure!", + "favorite": false, + "secureNote": { + "type": 0 + }, + "collectionIds": null + }"#; + + assert_eq!( + json.parse::().unwrap(), + expected.parse::().unwrap() + ) + } + + #[test] + fn test_convert_card() { + let cipher = Cipher { + id: "3ed8de45-48ee-4e26-a2dc-b10701276c53".parse().unwrap(), + folder_id: None, + + name: "My card".to_string(), + notes: None, + + r#type: CipherType::Card(Box::new(Card { + cardholder_name: Some("John Doe".to_string()), + exp_month: Some("1".to_string()), + exp_year: Some("2032".to_string()), + code: Some("123".to_string()), + brand: Some("Visa".to_string()), + number: Some("4111111111111111".to_string()), + })), + + favorite: false, + reprompt: 0, + + fields: vec![], + + revision_date: "2024-01-30T17:55:36.150Z".parse().unwrap(), + creation_date: "2024-01-30T17:55:36.150Z".parse().unwrap(), + deleted_date: None, + }; + + let json = serde_json::to_string(&JsonCipher::from(cipher)).unwrap(); + + let expected = r#"{ + "passwordHistory": null, + "revisionDate": "2024-01-30T17:55:36.150Z", + "creationDate": "2024-01-30T17:55:36.150Z", + "deletedDate": null, + "id": "3ed8de45-48ee-4e26-a2dc-b10701276c53", + "organizationId": null, + "folderId": null, + "type": 3, + "reprompt": 0, + "name": "My card", + "notes": null, + "favorite": false, + "card": { + "cardholderName": "John Doe", + "brand": "Visa", + "number": "4111111111111111", + "expMonth": "1", + "expYear": "2032", + "code": "123" + }, + "collectionIds": null + }"#; + + assert_eq!( + json.parse::().unwrap(), + expected.parse::().unwrap() + ) + } + + #[test] + fn test_convert_identity() { + let cipher = Cipher { + id: "41cc3bc1-c3d9-4637-876c-b10701273712".parse().unwrap(), + folder_id: Some("942e2984-1b9a-453b-b039-b107012713b9".parse().unwrap()), + + name: "My identity".to_string(), + notes: None, + + r#type: CipherType::Identity(Box::new(Identity { + title: Some("Mr".to_string()), + first_name: Some("John".to_string()), + middle_name: None, + last_name: Some("Doe".to_string()), + address1: None, + address2: None, + address3: None, + city: None, + state: None, + postal_code: None, + country: None, + company: Some("Bitwarden".to_string()), + email: None, + phone: None, + ssn: None, + username: Some("JDoe".to_string()), + passport_number: None, + license_number: None, + })), + + favorite: false, + reprompt: 0, + + fields: vec![], + + revision_date: "2024-01-30T17:54:50.706Z".parse().unwrap(), + creation_date: "2024-01-30T17:54:50.706Z".parse().unwrap(), + deleted_date: None, + }; + + let json = serde_json::to_string(&JsonCipher::from(cipher)).unwrap(); + + let expected = r#"{ + "passwordHistory": null, + "revisionDate": "2024-01-30T17:54:50.706Z", + "creationDate": "2024-01-30T17:54:50.706Z", + "deletedDate": null, + "id": "41cc3bc1-c3d9-4637-876c-b10701273712", + "organizationId": null, + "folderId": "942e2984-1b9a-453b-b039-b107012713b9", + "type": 4, + "reprompt": 0, + "name": "My identity", + "notes": null, + "favorite": false, + "identity": { + "title": "Mr", + "firstName": "John", + "middleName": null, + "lastName": "Doe", + "address1": null, + "address2": null, + "address3": null, + "city": null, + "state": null, + "postalCode": null, + "country": null, + "company": "Bitwarden", + "email": null, + "phone": null, + "ssn": null, + "username": "JDoe", + "passportNumber": null, + "licenseNumber": null + }, + "collectionIds": null + }"#; + + assert_eq!( + json.parse::().unwrap(), + expected.parse::().unwrap() + ) + } + + #[test] + pub fn test_export() { + let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + d.push("resources"); + d.push("json_export.json"); + + let mut file = fs::File::open(d).unwrap(); + + let mut expected = String::new(); + file.read_to_string(&mut expected).unwrap(); + + let export = export_json( + vec![Folder { + id: "942e2984-1b9a-453b-b039-b107012713b9".parse().unwrap(), + name: "Important".to_string(), + }], + vec![ + Cipher { + id: "25c8c414-b446-48e9-a1bd-b10700bbd740".parse().unwrap(), + folder_id: Some("942e2984-1b9a-453b-b039-b107012713b9".parse().unwrap()), + + name: "Bitwarden".to_string(), + notes: Some("My note".to_string()), + + r#type: CipherType::Login(Box::new(Login { + username: Some("test@bitwarden.com".to_string()), + password: Some("asdfasdfasdf".to_string()), + login_uris: vec![LoginUri { + uri: Some("https://vault.bitwarden.com".to_string()), + r#match: None, + }], + totp: Some("ABC".to_string()), + })), + + favorite: true, + reprompt: 0, + + fields: vec![ + Field { + name: Some("Text".to_string()), + value: Some("A".to_string()), + r#type: 0, + linked_id: None, + }, + Field { + name: Some("Hidden".to_string()), + value: Some("B".to_string()), + r#type: 1, + linked_id: None, + }, + Field { + name: Some("Boolean (true)".to_string()), + value: Some("true".to_string()), + r#type: 2, + linked_id: None, + }, + Field { + name: Some("Boolean (false)".to_string()), + value: Some("false".to_string()), + r#type: 2, + linked_id: None, + }, + Field { + name: Some("Linked".to_string()), + value: None, + r#type: 3, + linked_id: Some(101), + }, + ], + + revision_date: "2024-01-30T14:09:33.753Z".parse().unwrap(), + creation_date: "2024-01-30T11:23:54.416Z".parse().unwrap(), + deleted_date: None, + }, + Cipher { + id: "23f0f877-42b1-4820-a850-b10700bc41eb".parse().unwrap(), + folder_id: None, + + name: "My secure note".to_string(), + notes: Some("Very secure!".to_string()), + + r#type: CipherType::SecureNote(Box::new(SecureNote { + r#type: SecureNoteType::Generic, + })), + + favorite: false, + reprompt: 0, + + fields: vec![], + + revision_date: "2024-01-30T11:25:25.466Z".parse().unwrap(), + creation_date: "2024-01-30T11:25:25.466Z".parse().unwrap(), + deleted_date: None, + }, + Cipher { + id: "3ed8de45-48ee-4e26-a2dc-b10701276c53".parse().unwrap(), + folder_id: None, + + name: "My card".to_string(), + notes: None, + + r#type: CipherType::Card(Box::new(Card { + cardholder_name: Some("John Doe".to_string()), + exp_month: Some("1".to_string()), + exp_year: Some("2032".to_string()), + code: Some("123".to_string()), + brand: Some("Visa".to_string()), + number: Some("4111111111111111".to_string()), + })), + + favorite: false, + reprompt: 0, + + fields: vec![], + + revision_date: "2024-01-30T17:55:36.150Z".parse().unwrap(), + creation_date: "2024-01-30T17:55:36.150Z".parse().unwrap(), + deleted_date: None, + }, + Cipher { + id: "41cc3bc1-c3d9-4637-876c-b10701273712".parse().unwrap(), + folder_id: Some("942e2984-1b9a-453b-b039-b107012713b9".parse().unwrap()), + + name: "My identity".to_string(), + notes: None, + + r#type: CipherType::Identity(Box::new(Identity { + title: Some("Mr".to_string()), + first_name: Some("John".to_string()), + middle_name: None, + last_name: Some("Doe".to_string()), + address1: None, + address2: None, + address3: None, + city: None, + state: None, + postal_code: None, + country: None, + company: Some("Bitwarden".to_string()), + email: None, + phone: None, + ssn: None, + username: Some("JDoe".to_string()), + passport_number: None, + license_number: None, + })), + + favorite: false, + reprompt: 0, + + fields: vec![], + + revision_date: "2024-01-30T17:54:50.706Z".parse().unwrap(), + creation_date: "2024-01-30T17:54:50.706Z".parse().unwrap(), + deleted_date: None, + }, + ], + ) + .unwrap(); + + assert_eq!( + export.parse::().unwrap(), + expected.parse::().unwrap() + ) + } +} diff --git a/crates/bitwarden-exporters/src/lib.rs b/crates/bitwarden-exporters/src/lib.rs new file mode 100644 index 000000000..bb690fbc1 --- /dev/null +++ b/crates/bitwarden-exporters/src/lib.rs @@ -0,0 +1,142 @@ +use chrono::{DateTime, Utc}; +use thiserror::Error; +use uuid::Uuid; + +mod csv; +use csv::export_csv; +mod json; +use json::export_json; + +pub enum Format { + Csv, + Json, + EncryptedJson { password: String }, +} + +/// Export representation of a Bitwarden folder. +/// +/// These are mostly duplicated from the `bitwarden` vault models to facilitate a stable export API +/// that is not tied to the internal vault models. We may revisit this in the future. +pub struct Folder { + pub id: Uuid, + pub name: String, +} + +/// Export representation of a Bitwarden cipher. +/// +/// These are mostly duplicated from the `bitwarden` vault models to facilitate a stable export API +/// that is not tied to the internal vault models. We may revisit this in the future. +pub struct Cipher { + pub id: Uuid, + pub folder_id: Option, + + pub name: String, + pub notes: Option, + + pub r#type: CipherType, + + pub favorite: bool, + pub reprompt: u8, + + pub fields: Vec, + + pub revision_date: DateTime, + pub creation_date: DateTime, + pub deleted_date: Option>, +} + +#[derive(Clone)] +pub struct Field { + pub name: Option, + pub value: Option, + pub r#type: u8, + pub linked_id: Option, +} + +pub enum CipherType { + Login(Box), + SecureNote(Box), + Card(Box), + Identity(Box), +} + +impl ToString for CipherType { + fn to_string(&self) -> String { + match self { + CipherType::Login(_) => "login".to_string(), + CipherType::SecureNote(_) => "note".to_string(), + CipherType::Card(_) => "card".to_string(), + CipherType::Identity(_) => "identity".to_string(), + } + } +} + +pub struct Login { + pub username: Option, + pub password: Option, + pub login_uris: Vec, + pub totp: Option, +} + +pub struct LoginUri { + pub uri: Option, + pub r#match: Option, +} + +pub struct Card { + pub cardholder_name: Option, + pub exp_month: Option, + pub exp_year: Option, + pub code: Option, + pub brand: Option, + pub number: Option, +} + +pub struct SecureNote { + pub r#type: SecureNoteType, +} + +pub enum SecureNoteType { + Generic = 0, +} + +pub struct Identity { + pub title: Option, + pub first_name: Option, + pub middle_name: Option, + pub last_name: Option, + pub address1: Option, + pub address2: Option, + pub address3: Option, + pub city: Option, + pub state: Option, + pub postal_code: Option, + pub country: Option, + pub company: Option, + pub email: Option, + pub phone: Option, + pub ssn: Option, + pub username: Option, + pub passport_number: Option, + pub license_number: Option, +} + +#[derive(Error, Debug)] +pub enum ExportError { + #[error("CSV error: {0}")] + Csv(#[from] csv::CsvError), + #[error("JSON error: {0}")] + Json(#[from] json::JsonError), +} + +pub fn export( + folders: Vec, + ciphers: Vec, + format: Format, +) -> Result { + match format { + Format::Csv => Ok(export_csv(folders, ciphers)?), + Format::Json => Ok(export_json(folders, ciphers)?), + Format::EncryptedJson { password: _ } => todo!(), + } +} diff --git a/crates/bitwarden/Cargo.toml b/crates/bitwarden/Cargo.toml index d9508258b..c6f580282 100644 --- a/crates/bitwarden/Cargo.toml +++ b/crates/bitwarden/Cargo.toml @@ -31,6 +31,7 @@ base64 = ">=0.21.2, <0.22" bitwarden-api-api = { path = "../bitwarden-api-api", version = "=0.2.3" } bitwarden-api-identity = { path = "../bitwarden-api-identity", version = "=0.2.3" } bitwarden-crypto = { path = "../bitwarden-crypto", version = "=0.1.0" } +bitwarden-exporters = { path = "../bitwarden-exporters", version = "0.1.0" } bitwarden-generators = { path = "../bitwarden-generators", version = "0.1.0" } chrono = { version = ">=0.4.26, <0.5", features = [ "clock", diff --git a/crates/bitwarden/src/error.rs b/crates/bitwarden/src/error.rs index 173557b04..ed5d27c3e 100644 --- a/crates/bitwarden/src/error.rs +++ b/crates/bitwarden/src/error.rs @@ -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_exporters::ExportError; use bitwarden_generators::{PassphraseError, PasswordError, UsernameError}; use reqwest::StatusCode; use thiserror::Error; @@ -50,6 +51,7 @@ pub enum Error { #[error("The state file could not be read")] InvalidStateFile, + // Generators #[error(transparent)] UsernameError(#[from] UsernameError), #[error(transparent)] @@ -57,6 +59,9 @@ pub enum Error { #[error(transparent)] PasswordError(#[from] PasswordError), + #[error(transparent)] + ExportError(#[from] ExportError), + #[error("Internal error: {0}")] Internal(Cow<'static, str>), } diff --git a/crates/bitwarden/src/tool/exporters/client_exporter.rs b/crates/bitwarden/src/tool/exporters/client_exporter.rs index 9e0dfd5fc..05eb737f3 100644 --- a/crates/bitwarden/src/tool/exporters/client_exporter.rs +++ b/crates/bitwarden/src/tool/exporters/client_exporter.rs @@ -6,7 +6,7 @@ use crate::{ }; pub struct ClientExporters<'a> { - pub(crate) _client: &'a crate::Client, + pub(crate) client: &'a crate::Client, } impl<'a> ClientExporters<'a> { @@ -17,7 +17,7 @@ impl<'a> ClientExporters<'a> { ciphers: Vec, format: ExportFormat, ) -> Result { - export_vault(folders, ciphers, format) + export_vault(self.client, folders, ciphers, format) } pub async fn export_organization_vault( @@ -32,6 +32,6 @@ impl<'a> ClientExporters<'a> { impl<'a> Client { pub fn exporters(&'a self) -> ClientExporters<'a> { - ClientExporters { _client: self } + ClientExporters { client: self } } } diff --git a/crates/bitwarden/src/tool/exporters/mod.rs b/crates/bitwarden/src/tool/exporters/mod.rs index d03ddeb77..cbdb5bb86 100644 --- a/crates/bitwarden/src/tool/exporters/mod.rs +++ b/crates/bitwarden/src/tool/exporters/mod.rs @@ -1,8 +1,14 @@ +use bitwarden_crypto::Decryptable; +use bitwarden_exporters::export; use schemars::JsonSchema; use crate::{ - error::Result, - vault::{Cipher, Collection, Folder}, + error::{Error, Result}, + vault::{ + login::LoginUriView, Cipher, CipherType, CipherView, Collection, FieldView, Folder, + FolderView, SecureNoteType, + }, + Client, }; mod client_exporter; @@ -13,21 +19,26 @@ pub use client_exporter::ClientExporters; pub enum ExportFormat { Csv, Json, - AccountEncryptedJson, // TODO: Should we deprecate this option completely? EncryptedJson { password: String }, } pub(super) fn export_vault( - _folders: Vec, - _ciphers: Vec, + client: &Client, + folders: Vec, + ciphers: Vec, format: ExportFormat, ) -> Result { - Ok(match format { - ExportFormat::Csv => "Csv".to_owned(), - ExportFormat::Json => "Json".to_owned(), - ExportFormat::AccountEncryptedJson => "AccountEncryptedJson".to_owned(), - ExportFormat::EncryptedJson { .. } => "EncryptedJson".to_owned(), - }) + let enc = client.get_encryption_settings()?; + + let folders: Vec = folders.decrypt(enc, &None)?; + let folders: Vec = + folders.into_iter().flat_map(|f| f.try_into()).collect(); + + let ciphers: Vec = ciphers.decrypt(enc, &None)?; + let ciphers: Vec = + ciphers.into_iter().flat_map(|c| c.try_into()).collect(); + + Ok(export(folders, ciphers, format.into())?) } pub(super) fn export_organization_vault( @@ -37,3 +48,248 @@ pub(super) fn export_organization_vault( ) -> Result { todo!(); } + +impl TryFrom for bitwarden_exporters::Folder { + type Error = Error; + + fn try_from(value: FolderView) -> Result { + Ok(Self { + id: value.id.ok_or(Error::MissingFields)?, + name: value.name, + }) + } +} + +impl TryFrom for bitwarden_exporters::Cipher { + type Error = Error; + + fn try_from(value: CipherView) -> Result { + let r = match value.r#type { + CipherType::Login => { + let l = value.login.ok_or(Error::MissingFields)?; + bitwarden_exporters::CipherType::Login(Box::new(bitwarden_exporters::Login { + username: l.username, + password: l.password, + login_uris: l + .uris + .unwrap_or_default() + .into_iter() + .map(|u| u.into()) + .collect(), + totp: l.totp, + })) + } + CipherType::SecureNote => bitwarden_exporters::CipherType::SecureNote(Box::new( + bitwarden_exporters::SecureNote { + r#type: value + .secure_note + .map(|t| t.r#type) + .unwrap_or(SecureNoteType::Generic) + .into(), + }, + )), + CipherType::Card => { + let c = value.card.ok_or(Error::MissingFields)?; + bitwarden_exporters::CipherType::Card(Box::new(bitwarden_exporters::Card { + cardholder_name: c.cardholder_name, + exp_month: c.exp_month, + exp_year: c.exp_year, + code: c.code, + brand: c.brand, + number: c.number, + })) + } + CipherType::Identity => { + let i = value.identity.ok_or(Error::MissingFields)?; + bitwarden_exporters::CipherType::Identity(Box::new(bitwarden_exporters::Identity { + title: i.title, + first_name: i.first_name, + middle_name: i.middle_name, + last_name: i.last_name, + address1: i.address1, + address2: i.address2, + address3: i.address3, + city: i.city, + state: i.state, + postal_code: i.postal_code, + country: i.country, + company: i.company, + email: i.email, + phone: i.phone, + ssn: i.ssn, + username: i.username, + passport_number: i.passport_number, + license_number: i.license_number, + })) + } + }; + + Ok(Self { + id: value.id.ok_or(Error::MissingFields)?, + folder_id: value.folder_id, + name: value.name, + notes: value.notes, + r#type: r, + favorite: value.favorite, + reprompt: value.reprompt as u8, + fields: value + .fields + .unwrap_or_default() + .into_iter() + .map(|f| f.into()) + .collect(), + revision_date: value.revision_date, + creation_date: value.creation_date, + deleted_date: value.deleted_date, + }) + } +} + +impl From for bitwarden_exporters::Field { + fn from(value: FieldView) -> Self { + Self { + name: value.name, + value: value.value, + r#type: value.r#type as u8, + linked_id: value.linked_id.map(|id| id.into()), + } + } +} + +impl From for bitwarden_exporters::LoginUri { + fn from(value: LoginUriView) -> Self { + Self { + r#match: value.r#match.map(|v| v as u8), + uri: value.uri, + } + } +} + +impl From for bitwarden_exporters::SecureNoteType { + fn from(value: SecureNoteType) -> Self { + match value { + SecureNoteType::Generic => bitwarden_exporters::SecureNoteType::Generic, + } + } +} + +impl From for bitwarden_exporters::Format { + fn from(value: ExportFormat) -> Self { + match value { + ExportFormat::Csv => Self::Csv, + ExportFormat::Json => Self::Json, + ExportFormat::EncryptedJson { password } => Self::EncryptedJson { password }, + } + } +} + +#[cfg(test)] +mod tests { + use chrono::{DateTime, Utc}; + + use super::*; + use crate::vault::{login::LoginView, CipherRepromptType}; + + #[test] + fn test_try_from_folder_view() { + let view = FolderView { + id: Some("fd411a1a-fec8-4070-985d-0e6560860e69".parse().unwrap()), + name: "test_name".to_string(), + revision_date: "2024-01-30T17:55:36.150Z".parse().unwrap(), + }; + + let f: bitwarden_exporters::Folder = view.try_into().unwrap(); + + assert_eq!( + f.id, + "fd411a1a-fec8-4070-985d-0e6560860e69".parse().unwrap() + ); + assert_eq!(f.name, "test_name".to_string()); + } + + #[test] + fn test_try_from_cipher_view_login() { + let cipher_view = CipherView { + r#type: CipherType::Login, + login: Some(LoginView { + username: Some("test_username".to_string()), + password: Some("test_password".to_string()), + password_revision_date: None, + uris: None, + totp: None, + autofill_on_page_load: None, + }), + id: "fd411a1a-fec8-4070-985d-0e6560860e69".parse().ok(), + organization_id: None, + folder_id: None, + collection_ids: vec![], + key: None, + name: "My login".to_string(), + notes: None, + identity: None, + card: None, + secure_note: None, + favorite: false, + reprompt: CipherRepromptType::None, + organization_use_totp: true, + edit: true, + view_password: true, + local_data: None, + attachments: None, + fields: None, + password_history: None, + creation_date: "2024-01-30T17:55:36.150Z".parse().unwrap(), + deleted_date: None, + revision_date: "2024-01-30T17:55:36.150Z".parse().unwrap(), + }; + + let cipher: bitwarden_exporters::Cipher = cipher_view.try_into().unwrap(); + + assert_eq!( + cipher.id, + "fd411a1a-fec8-4070-985d-0e6560860e69".parse().unwrap() + ); + assert_eq!(cipher.folder_id, None); + assert_eq!(cipher.name, "My login".to_string()); + assert_eq!(cipher.notes, None); + assert!(!cipher.favorite); + assert_eq!(cipher.reprompt, 0); + assert!(cipher.fields.is_empty()); + assert_eq!( + cipher.revision_date, + "2024-01-30T17:55:36.150Z".parse::>().unwrap() + ); + assert_eq!( + cipher.creation_date, + "2024-01-30T17:55:36.150Z".parse::>().unwrap() + ); + assert_eq!(cipher.deleted_date, None); + + if let bitwarden_exporters::CipherType::Login(l) = cipher.r#type { + assert_eq!(l.username, Some("test_username".to_string())); + assert_eq!(l.password, Some("test_password".to_string())); + assert!(l.login_uris.is_empty()); + assert_eq!(l.totp, None); + } else { + panic!("Expected login type"); + } + } + + #[test] + fn test_from_export_format() { + assert!(matches!( + bitwarden_exporters::Format::from(ExportFormat::Csv), + bitwarden_exporters::Format::Csv + )); + assert!(matches!( + bitwarden_exporters::Format::from(ExportFormat::Json), + bitwarden_exporters::Format::Json + )); + assert!(matches!( + bitwarden_exporters::Format::from(ExportFormat::EncryptedJson { + password: "password".to_string() + }), + bitwarden_exporters::Format::EncryptedJson { .. } + )); + } +} diff --git a/crates/bitwarden/src/vault/cipher/field.rs b/crates/bitwarden/src/vault/cipher/field.rs index db896474f..bde713a05 100644 --- a/crates/bitwarden/src/vault/cipher/field.rs +++ b/crates/bitwarden/src/vault/cipher/field.rs @@ -34,11 +34,11 @@ pub struct Field { #[serde(rename_all = "camelCase", deny_unknown_fields)] #[cfg_attr(feature = "mobile", derive(uniffi::Record))] pub struct FieldView { - name: Option, - value: Option, - r#type: FieldType, + pub(crate) name: Option, + pub(crate) value: Option, + pub(crate) r#type: FieldType, - linked_id: Option, + pub(crate) linked_id: Option, } impl KeyEncryptable for FieldView { diff --git a/crates/bitwarden/src/vault/cipher/linked_id.rs b/crates/bitwarden/src/vault/cipher/linked_id.rs index 6fb676dfe..77429438e 100644 --- a/crates/bitwarden/src/vault/cipher/linked_id.rs +++ b/crates/bitwarden/src/vault/cipher/linked_id.rs @@ -25,7 +25,13 @@ impl UniffiCustomTypeConverter for LinkedIdType { } fn from_custom(obj: Self) -> Self::Builtin { - serde_json::to_value(obj) + obj.into() + } +} + +impl From for u32 { + fn from(v: LinkedIdType) -> Self { + serde_json::to_value(v) .expect("LinkedIdType should be serializable") .as_u64() .expect("Not a numeric enum value") as u32 diff --git a/crates/bitwarden/src/vault/cipher/mod.rs b/crates/bitwarden/src/vault/cipher/mod.rs index c891f439d..c2b49eb37 100644 --- a/crates/bitwarden/src/vault/cipher/mod.rs +++ b/crates/bitwarden/src/vault/cipher/mod.rs @@ -9,4 +9,9 @@ pub(crate) mod local_data; pub(crate) mod login; pub(crate) mod secure_note; -pub use cipher::{Cipher, CipherListView, CipherView}; +pub use attachment::{ + Attachment, AttachmentEncryptResult, AttachmentFile, AttachmentFileView, AttachmentView, +}; +pub use cipher::{Cipher, CipherListView, CipherRepromptType, CipherType, CipherView}; +pub use field::FieldView; +pub use secure_note::SecureNoteType; diff --git a/crates/bitwarden/src/vault/cipher/secure_note.rs b/crates/bitwarden/src/vault/cipher/secure_note.rs index 8f7069ee1..2433a9c2a 100644 --- a/crates/bitwarden/src/vault/cipher/secure_note.rs +++ b/crates/bitwarden/src/vault/cipher/secure_note.rs @@ -24,7 +24,7 @@ pub struct SecureNote { #[serde(rename_all = "camelCase", deny_unknown_fields)] #[cfg_attr(feature = "mobile", derive(uniffi::Record))] pub struct SecureNoteView { - r#type: SecureNoteType, + pub(crate) r#type: SecureNoteType, } impl KeyEncryptable for SecureNoteView { diff --git a/crates/bitwarden/src/vault/folder.rs b/crates/bitwarden/src/vault/folder.rs index 17d1d40aa..edd1cac42 100644 --- a/crates/bitwarden/src/vault/folder.rs +++ b/crates/bitwarden/src/vault/folder.rs @@ -22,9 +22,9 @@ pub struct Folder { #[serde(rename_all = "camelCase")] #[cfg_attr(feature = "mobile", derive(uniffi::Record))] pub struct FolderView { - id: Option, - name: String, - revision_date: DateTime, + pub id: Option, + pub name: String, + pub revision_date: DateTime, } impl LocateKey for FolderView {} diff --git a/crates/bitwarden/src/vault/mod.rs b/crates/bitwarden/src/vault/mod.rs index dce7b0e04..2addfec6b 100644 --- a/crates/bitwarden/src/vault/mod.rs +++ b/crates/bitwarden/src/vault/mod.rs @@ -6,12 +6,7 @@ mod send; #[cfg(feature = "mobile")] mod totp; -pub use cipher::{ - attachment::{ - Attachment, AttachmentEncryptResult, AttachmentFile, AttachmentFileView, AttachmentView, - }, - Cipher, CipherListView, CipherView, -}; +pub use cipher::*; pub use collection::{Collection, CollectionView}; pub use folder::{Folder, FolderView}; pub use password_history::{PasswordHistory, PasswordHistoryView}; From 1595306d0f80807983c374aaebc4938a835be78c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ch=C4=99ci=C5=84ski?= Date: Tue, 6 Feb 2024 18:02:33 +0100 Subject: [PATCH 34/34] [DEVOPS-1711] Add Docker image for bws versioning for release workflow (#573) ## Type of change ``` - [ ] Bug fix - [ ] New feature development - [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc) - [x] Build/deploy pipeline (DevOps) - [ ] Other ``` ## Objective Add docker publish to `release-cli.yml` workflow. ## Code changes - **.github/workflows/release-cli.yml:** Add docker publish job to `release-cli.yml` workflow. - **.github/workflows/build-cli-docker.yml** Remove unused input. Publish docker only if building for publish branch ## Before you submit - Please add **unit tests** where it makes sense to do so --------- Co-authored-by: Vince Grassia <593223+vgrassia@users.noreply.github.com> --- .github/workflows/build-cli-docker.yml | 7 +-- .github/workflows/release-cli.yml | 85 ++++++++++++++++++++++++-- 2 files changed, 82 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build-cli-docker.yml b/.github/workflows/build-cli-docker.yml index c0aa62664..5cee3899b 100644 --- a/.github/workflows/build-cli-docker.yml +++ b/.github/workflows/build-cli-docker.yml @@ -6,11 +6,6 @@ on: paths: - "crates/bws/**" workflow_dispatch: - inputs: - sdk_branch: - description: "Server branch name to deploy (examples: 'master', 'rc', 'feature/sm')" - type: string - default: master pull_request: paths: - ".github/workflows/build-cli-docker.yml" @@ -111,7 +106,7 @@ jobs: platforms: | linux/amd64, linux/arm64/v8 - push: true + push: ${{ env.is_publish_branch }} tags: ${{ steps.tag-list.outputs.tags }} secrets: | "GH_PAT=${{ steps.retrieve-secret-pat.outputs.github-pat-bitwarden-devops-bot-repo-scope }}" diff --git a/.github/workflows/release-cli.yml b/.github/workflows/release-cli.yml index a6059a1d5..fa1ffc346 100644 --- a/.github/workflows/release-cli.yml +++ b/.github/workflows/release-cli.yml @@ -8,17 +8,19 @@ on: release_type: description: "Release Options" required: true - default: "Initial Release" + default: "Release" type: choice options: - - Initial Release - - Redeploy + - Release - Dry Run defaults: run: shell: bash +env: + _AZ_REGISTRY: bitwardenprod.azurecr.io + jobs: setup: name: Setup @@ -120,7 +122,7 @@ jobs: publish: name: Publish bws to crates.io - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: - setup steps: @@ -156,3 +158,78 @@ jobs: PUBLISH_GRACE_SLEEP: 10 CARGO_REGISTRY_TOKEN: ${{ steps.retrieve-secrets.outputs.cratesio-api-token }} run: cargo-release release publish -p bws --execute --no-confirm + + publish-docker: + name: Publish docker versioned and latest image + runs-on: ubuntu-22.04 + needs: setup + steps: + - name: Checkout + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: Generate tag list + id: tag-list + env: + VERSION: ${{ needs.setup.outputs.release-version }} + DRY_RUN: ${{ inputs.release_type == 'Dry Run' }} + run: | + if [[ "${DRY_RUN}" == "true" ]]; then + REF=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}} + IMAGE_TAG=$(echo "${REF}" | sed "s#/#-#g") # slash safe branch name + echo "tags=$_AZ_REGISTRY/bws:${IMAGE_TAG},bitwarden/bws:${IMAGE_TAG}" >> $GITHUB_OUTPUT + else + echo "tags=$_AZ_REGISTRY/bws:${VERSION},bitwarden/bws:${VERSION},$_AZ_REGISTRY/bws:latest,bitwarden/bws:latest" >> $GITHUB_OUTPUT + fi + + ########## Set up Docker ########## + - name: Set up QEMU emulators + uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 + + ########## Login to Docker registries ########## + - name: Login to Azure - Prod Subscription + uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0 + with: + creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }} + + - name: Login to Azure ACR + run: az acr login -n ${_AZ_REGISTRY%.azurecr.io} + + - name: Login to Azure - CI Subscription + uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0 + with: + creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} + + - name: Retrieve github PAT secrets + id: retrieve-secret-pat + uses: bitwarden/gh-actions/get-keyvault-secrets@main + with: + keyvault: "bitwarden-ci" + secrets: "github-pat-bitwarden-devops-bot-repo-scope" + + - name: Setup Docker Trust + uses: bitwarden/gh-actions/setup-docker-trust@main + with: + azure-creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} + azure-keyvault-name: "bitwarden-ci" + + - name: Build and push Docker image + uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5.1.0 + with: + context: . + file: crates/bws/Dockerfile + platforms: | + linux/amd64, + linux/arm64/v8 + push: ${{ inputs.release_type != 'Dry Run' }} + tags: ${{ steps.tag-list.outputs.tags }} + secrets: | + "GH_PAT=${{ steps.retrieve-secret-pat.outputs.github-pat-bitwarden-devops-bot-repo-scope }}" + + - name: Log out of Docker and disable Docker Notary + if: ${{ github.event.inputs.release_type != 'Dry Run' }} + run: | + docker logout + echo "DOCKER_CONTENT_TRUST=0" >> $GITHUB_ENV