diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..632eb5ff8 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,9 @@ +# Please sort into logical groups with comment headers. Sort groups in order of specificity. +# For example, default owners should always be the first group. +# Sort lines alphabetically within these groups to avoid accidentally adding duplicates. +# +# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners + + +# DevOps for Actions and other workflow changes. +.github/workflows @bitwarden/dept-devops diff --git a/.github/workflows/build-android.yml b/.github/workflows/build-android.yml index 00e1fffce..4fd1cc975 100644 --- a/.github/workflows/build-android.yml +++ b/.github/workflows/build-android.yml @@ -25,15 +25,15 @@ jobs: - target: i686-linux-android steps: - name: Checkout repo - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Install rust - uses: dtolnay/rust-toolchain@f361669954a8ecfc00a3443f35f9ac8e610ffc06 # stable + uses: dtolnay/rust-toolchain@439cf607258077187679211f12aa6f19af4a0af7 # stable with: toolchain: 1.67.0 # https://github.com/cross-rs/cross/issues/1222 - name: Cache cargo registry - uses: Swatinem/rust-cache@e207df5d269b42b69c8bc5101da26f7d31feddb4 # v2.6.2 + uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43 # v2.7.0 with: key: ${{ matrix.settings.target }}-cargo @@ -46,7 +46,7 @@ jobs: run: cross build -p bitwarden-uniffi --release --target=${{ matrix.settings.target }} - name: Upload artifact - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: android-${{ matrix.settings.target }} path: ./target/${{ matrix.settings.target }}/release/libbitwarden_uniffi.so @@ -57,30 +57,30 @@ jobs: needs: build steps: - name: Checkout repo (PR) - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 if: github.event_name == 'pull_request' with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.ref }} - name: Checkout repo (Push) - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 if: github.event_name == 'push' with: fetch-depth: 0 - name: Install rust - uses: dtolnay/rust-toolchain@f361669954a8ecfc00a3443f35f9ac8e610ffc06 # stable + uses: dtolnay/rust-toolchain@439cf607258077187679211f12aa6f19af4a0af7 # stable with: - toolchain: 1.67.0 # https://github.com/cross-rs/cross/issues/1222 + toolchain: stable - name: Cache cargo registry - uses: Swatinem/rust-cache@e207df5d269b42b69c8bc5101da26f7d31feddb4 # v2.6.2 + uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43 # v2.7.0 with: key: cargo-combine-cache - name: Setup Java - uses: actions/setup-java@cd89f46ac9d01407894225f350157564c9c7cee2 # v3.12.0 + uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3.13.0 with: distribution: temurin java-version: 17 @@ -101,23 +101,10 @@ jobs: working-directory: languages/kotlin run: ./build-schemas.sh - - name: Login to Azure - CI Subscription - uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 - with: - creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - - - name: Retrieve bot secrets - id: retrieve-bot-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@f096207b7a2f31723165aee6ad03e91716686e78 - with: - keyvault: bitwarden-ci - secrets: "github-pat-bitwarden-devops-bot-packages-scope" - - name: Publish - uses: gradle/gradle-build-action@243af859f8ca30903d9d7f7936897ca0358ba691 # v2.7.1 + uses: gradle/gradle-build-action@842c587ad8aa4c68eeba24c396e15af4c2e9f30a # v2.9.0 with: arguments: sdk:publish build-root-directory: languages/kotlin env: - GITHUB_ACTOR: "bitwarden-devops-bot" - GITHUB_TOKEN: ${{ steps.retrieve-bot-secrets.outputs.github-pat-bitwarden-devops-bot-packages-scope }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml index 132c67cd6..624a69c4f 100644 --- a/.github/workflows/build-cli.yml +++ b/.github/workflows/build-cli.yml @@ -22,7 +22,7 @@ jobs: package_version: ${{ steps.retrieve-version.outputs.package_version }} steps: - name: Checkout repo - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Get Package Version id: retrieve-version @@ -60,16 +60,16 @@ jobs: target: aarch64-unknown-linux-gnu steps: - name: Checkout repo - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Install rust - uses: dtolnay/rust-toolchain@f361669954a8ecfc00a3443f35f9ac8e610ffc06 # stable + uses: dtolnay/rust-toolchain@439cf607258077187679211f12aa6f19af4a0af7 # stable with: toolchain: stable targets: ${{ matrix.settings.target }} - name: Cache cargo registry - uses: Swatinem/rust-cache@e207df5d269b42b69c8bc5101da26f7d31feddb4 # v2.6.2 + uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43 # v2.7.0 with: key: ${{ matrix.settings.target }}-cargo-${{ matrix.settings.os }} @@ -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@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: bws-${{ matrix.settings.target }}-${{ env._PACKAGE_VERSION }}.zip path: ./bws-${{ matrix.settings.target }}-${{ env._PACKAGE_VERSION }}.zip @@ -115,7 +115,7 @@ jobs: _PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }} steps: - name: Checkout repo - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Download x86_64-apple-darwin artifact uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 @@ -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@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: bws-macos-universal-${{ env._PACKAGE_VERSION }}.zip path: ./bws-macos-universal-${{ env._PACKAGE_VERSION }}.zip @@ -155,15 +155,15 @@ jobs: - setup steps: - name: Checkout repo - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Install rust - uses: dtolnay/rust-toolchain@f361669954a8ecfc00a3443f35f9ac8e610ffc06 # stable + uses: dtolnay/rust-toolchain@439cf607258077187679211f12aa6f19af4a0af7 # stable with: toolchain: stable - name: Cache cargo registry - uses: Swatinem/rust-cache@e207df5d269b42b69c8bc5101da26f7d31feddb4 # v2.6.2 + uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43 # v2.7.0 with: key: cargo-cli-about @@ -177,7 +177,7 @@ jobs: sed -i.bak 's/\$NAME\$/Bitwarden Secrets Manager CLI/g' THIRDPARTY.html - name: Upload artifact - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: THIRDPARTY.html path: ./crates/bws/THIRDPARTY.html diff --git a/.github/workflows/build-dotnet.yml b/.github/workflows/build-dotnet.yml new file mode 100644 index 000000000..608e8c47d --- /dev/null +++ b/.github/workflows/build-dotnet.yml @@ -0,0 +1,78 @@ +name: Build .NET SDK + +on: + pull_request: + branches: + - master + +jobs: + generate_schemas: + uses: ./.github/workflows/generate_schemas.yml + + build_rust: + uses: ./.github/workflows/build-rust-cross-platform.yml + + build_dotnet: + name: Build .NET + runs-on: ubuntu-22.04 + needs: + - generate_schemas + - build_rust + + steps: + - name: Checkout Repository + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 + + - name: Download C# schemas artifact + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + with: + name: schemas.cs + path: languages/csharp/Bitwarden.Sdk + + - name: Set up .NET Core + uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0 + with: + global-json-file: languages/csharp/global.json + + - name: Download x86_64-apple-darwin files + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + with: + name: libbitwarden_c_files-x86_64-apple-darwin + path: languages/csharp/Bitwarden.Sdk/macos-x64 + + - name: Download aarch64-apple-darwin files + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + with: + name: libbitwarden_c_files-aarch64-apple-darwin + path: languages/csharp/Bitwarden.Sdk/macos-arm64 + + - name: Download x86_64-unknown-linux-gnu files + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + with: + name: libbitwarden_c_files-x86_64-unknown-linux-gnu + path: languages/csharp/Bitwarden.Sdk/ubuntu-x64 + + - name: Download x86_64-pc-windows-msvc files + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + with: + name: libbitwarden_c_files-x86_64-pc-windows-msvc + path: languages/csharp/Bitwarden.Sdk/windows-x64 + + - name: Build .NET 6 Project + working-directory: languages/csharp/Bitwarden.Sdk + run: | + dotnet restore + dotnet build --configuration Release + + - name: Pack NuGet Package + env: + VERSION: 0.0.1 + run: dotnet pack --configuration Release -p:PackageID=Bitwarden.Sdk -p:Version=${VERSION} --output ./nuget-output /nologo /v:n + working-directory: languages/csharp/Bitwarden.Sdk + + - name: Upload NuGet package + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 + with: + name: Bitwarden.Sdk.0.0.1.nupkg + path: | + ./languages/csharp/Bitwarden.Sdk/nuget-output/*.nupkg diff --git a/.github/workflows/build-napi.yml b/.github/workflows/build-napi.yml index b2433e4b5..b55ef939f 100644 --- a/.github/workflows/build-napi.yml +++ b/.github/workflows/build-napi.yml @@ -51,7 +51,7 @@ jobs: strip *.node steps: - name: Checkout repo - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Setup Node uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1 @@ -61,13 +61,13 @@ jobs: cache-dependency-path: crates/bitwarden-napi/package-lock.json - name: Install rust - uses: dtolnay/rust-toolchain@f361669954a8ecfc00a3443f35f9ac8e610ffc06 # stable + uses: dtolnay/rust-toolchain@439cf607258077187679211f12aa6f19af4a0af7 # stable with: toolchain: stable targets: ${{ matrix.settings.target }} - name: Cache cargo registry - uses: Swatinem/rust-cache@e207df5d269b42b69c8bc5101da26f7d31feddb4 # v2.6.2 + uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43 # v2.7.0 with: key: ${{ matrix.settings.target }}-cargo-${{ matrix.settings.os }} @@ -84,7 +84,7 @@ jobs: run: ${{ matrix.settings.build }} - name: Upload artifact - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: sdk-bitwarden-napi-${{ matrix.settings.target }} path: ${{ github.workspace }}/crates/bitwarden-napi/sdk-napi.*.node diff --git a/.github/workflows/build-rust-crates.yml b/.github/workflows/build-rust-crates.yml index d170e93c2..0b868367d 100644 --- a/.github/workflows/build-rust-crates.yml +++ b/.github/workflows/build-rust-crates.yml @@ -34,16 +34,16 @@ jobs: steps: - name: Checkout - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Install rust - uses: dtolnay/rust-toolchain@f361669954a8ecfc00a3443f35f9ac8e610ffc06 # stable + uses: dtolnay/rust-toolchain@439cf607258077187679211f12aa6f19af4a0af7 # stable with: toolchain: stable targets: ${{ matrix.settings.target }} - name: Cache cargo registry - uses: Swatinem/rust-cache@e207df5d269b42b69c8bc5101da26f7d31feddb4 # v2.6.2 + uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43 # v2.7.0 - name: Build run: cargo build -p ${{ matrix.package }} --release @@ -64,16 +64,16 @@ jobs: - build steps: - name: Checkout - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Install rust - uses: dtolnay/rust-toolchain@f361669954a8ecfc00a3443f35f9ac8e610ffc06 # stable + uses: dtolnay/rust-toolchain@439cf607258077187679211f12aa6f19af4a0af7 # stable with: toolchain: stable targets: ${{ matrix.settings.target }} - name: Cache cargo registry - uses: Swatinem/rust-cache@e207df5d269b42b69c8bc5101da26f7d31feddb4 # v2.6.2 + uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43 # v2.7.0 - name: Install cargo-release run: cargo install cargo-release diff --git a/.github/workflows/build-rust-cross-platform.yml b/.github/workflows/build-rust-cross-platform.yml new file mode 100644 index 000000000..007cf5d17 --- /dev/null +++ b/.github/workflows/build-rust-cross-platform.yml @@ -0,0 +1,48 @@ +name: Build Rust Cross Platform + +on: + workflow_call: + +jobs: + build_rust: + name: Build for ${{ matrix.settings.os }} ${{ matrix.settings.target }} + runs-on: ${{ matrix.settings.os }} + strategy: + fail-fast: false + matrix: + settings: + - os: macos-12 + target: x86_64-apple-darwin + - os: macos-12 + target: aarch64-apple-darwin + - os: windows-2022 + target: x86_64-pc-windows-msvc + - os: ubuntu-22.04 + target: x86_64-unknown-linux-gnu + + steps: + - name: Checkout + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 + + - name: Install rust + uses: dtolnay/rust-toolchain@439cf607258077187679211f12aa6f19af4a0af7 # stable + with: + toolchain: stable + + - name: Cache cargo registry + uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43 # v2.7.0 + + - name: Add build architecture + run: rustup target add ${{ matrix.settings.target }} + + - name: Build Rust + env: + RUSTFLAGS: "-D warnings" + run: cargo build --target ${{ matrix.settings.target }} --release + + - name: Upload Artifact + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 + with: + name: libbitwarden_c_files-${{ matrix.settings.target }} + path: | + target/${{ matrix.settings.target }}/release/*bitwarden_c* diff --git a/.github/workflows/cloc.yml b/.github/workflows/cloc.yml index 6b0672dcb..48f72ff65 100644 --- a/.github/workflows/cloc.yml +++ b/.github/workflows/cloc.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout repo - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Set up cloc run: | diff --git a/.github/workflows/delete-old-packages.yml b/.github/workflows/delete-old-packages.yml new file mode 100644 index 000000000..517560ede --- /dev/null +++ b/.github/workflows/delete-old-packages.yml @@ -0,0 +1,25 @@ +--- +name: Delete old packages + +on: + schedule: + - cron: "0 0 * * *" + workflow_dispatch: + +defaults: + run: + shell: bash + +jobs: + delete: + name: Cleanup Android SDK + runs-on: ubuntu-22.04 + steps: + - uses: actions/delete-package-versions@0d39a63126868f5eefaa47169615edd3c0f61e20 # v4.1.1 + with: + package-name: com.bitwarden.sdk-android + package-type: maven + min-versions-to-keep: 25 + + # Ignore versions only containing version numbers + ignore-versions: '^\\d*\\.\\d*\\.\\d*$' diff --git a/.github/workflows/direct-minimal-versions.yml b/.github/workflows/direct-minimal-versions.yml index ab5462f22..579817c49 100644 --- a/.github/workflows/direct-minimal-versions.yml +++ b/.github/workflows/direct-minimal-versions.yml @@ -36,16 +36,16 @@ jobs: steps: - name: Checkout - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Install rust - uses: dtolnay/rust-toolchain@f361669954a8ecfc00a3443f35f9ac8e610ffc06 # stable + uses: dtolnay/rust-toolchain@439cf607258077187679211f12aa6f19af4a0af7 # stable with: toolchain: nightly targets: ${{ matrix.settings.target }} - name: Cache cargo registry - uses: Swatinem/rust-cache@e207df5d269b42b69c8bc5101da26f7d31feddb4 # v2.6.2 + uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43 # v2.7.0 with: key: dmv-${{ matrix.settings.target }}-cargo-${{ matrix.settings.os }} diff --git a/.github/workflows/generate_schemas.yml b/.github/workflows/generate_schemas.yml index a391cfb83..9b2ac57c3 100644 --- a/.github/workflows/generate_schemas.yml +++ b/.github/workflows/generate_schemas.yml @@ -13,10 +13,10 @@ jobs: steps: - name: Checkout - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Install rust - uses: dtolnay/rust-toolchain@f361669954a8ecfc00a3443f35f9ac8e610ffc06 # stable + uses: dtolnay/rust-toolchain@439cf607258077187679211f12aa6f19af4a0af7 # stable with: toolchain: stable @@ -31,34 +31,34 @@ jobs: run: npm ci - name: Cache cargo registry - uses: Swatinem/rust-cache@e207df5d269b42b69c8bc5101da26f7d31feddb4 # v2.6.2 + uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43 # v2.7.0 - name: NPM Schemas run: npm run schemas - name: Upload ts schemas artifact - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: schemas.ts path: ${{ github.workspace }}/languages/js_webassembly/bitwarden_client/schemas.ts if-no-files-found: error - name: Upload c# schemas artifact - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: schemas.cs - path: ${{ github.workspace }}/languages/csharp/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@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: schemas.py path: ${{ github.workspace }}/languages/python/BitwardenClient/schemas.py if-no-files-found: error - name: Upload json schemas artifact - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: sdk-schemas-json path: ${{ github.workspace }}/support/schemas/* diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index aeebb60cd..425f4e83a 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -17,15 +17,15 @@ jobs: steps: - name: Checkout - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Install rust - uses: dtolnay/rust-toolchain@f361669954a8ecfc00a3443f35f9ac8e610ffc06 # stable + uses: dtolnay/rust-toolchain@439cf607258077187679211f12aa6f19af4a0af7 # stable with: toolchain: stable - name: Cache cargo registry - uses: Swatinem/rust-cache@e207df5d269b42b69c8bc5101da26f7d31feddb4 # v2.6.2 + uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43 # v2.7.0 - name: Cargo fmt run: cargo fmt --check diff --git a/.github/workflows/publish-dotnet.yml b/.github/workflows/publish-dotnet.yml new file mode 100644 index 000000000..7f438b24c --- /dev/null +++ b/.github/workflows/publish-dotnet.yml @@ -0,0 +1,69 @@ +name: Deploy NuGet Package + +on: + workflow_dispatch: + version_number: + description: "New Version" + required: true + +jobs: + generate_schemas: + uses: ./.github/workflows/generate_schemas.yml + + build_rust: + uses: ./.github/workflows/build-rust-cross-platform.yml + + deploy: + name: Deploy + runs-on: ubuntu-22.04 + needs: + - generate_schemas + - build_rust + + steps: + - name: Checkout Repository + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 + + - name: Download C# schemas artifact + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + with: + name: schemas.cs + path: languages/csharp/Bitwarden.Sdk + + - name: Set up .NET Core + uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0 + with: + global-json-file: languages/csharp/global.json + + - name: Download x86_64-apple-darwin files + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + with: + name: libbitwarden_c_files-x86_64-apple-darwin + path: languages/csharp/Bitwarden.Sdk/macos-x64 + + - name: Download aarch64-apple-darwin files + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + with: + name: libbitwarden_c_files-aarch64-apple-darwin + path: languages/csharp/Bitwarden.Sdk/macos-arm64 + + - name: Download x86_64-unknown-linux-gnu files + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + with: + name: libbitwarden_c_files-x86_64-unknown-linux-gnu + path: languages/csharp/Bitwarden.Sdk/ubuntu-x64 + + - name: Download x86_64-pc-windows-msvc files + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + with: + name: libbitwarden_c_files-x86_64-pc-windows-msvc + path: languages/csharp/Bitwarden.Sdk/windows-x64 + + - name: Pack NuGet Package + env: + VERSION: ${{ github.event.inputs.version_number }} + run: dotnet pack --configuration Release -p:PackageID=Bitwarden.Sdk -p:Version=${VERSION} --output ./nuget-output /nologo /v:n + working-directory: languages/csharp/Bitwarden.Sdk + + - name: Publish NuGet Package + run: dotnet nuget push ./languages/csharp/Bitwarden.Sdk/nuget-output/*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json diff --git a/.github/workflows/publish-rust-crates.yml b/.github/workflows/publish-rust-crates.yml index 14d846545..4cef3fe63 100644 --- a/.github/workflows/publish-rust-crates.yml +++ b/.github/workflows/publish-rust-crates.yml @@ -43,7 +43,7 @@ jobs: packages_command: ${{ steps.packages-list.outputs.packages_command }} steps: - name: Checkout repo - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Branch check if: ${{ github.event.inputs.release_type != 'Dry Run' }} @@ -100,7 +100,7 @@ jobs: - setup steps: - name: Checkout - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Login to Azure uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 @@ -109,18 +109,18 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@f096207b7a2f31723165aee6ad03e91716686e78 + uses: bitwarden/gh-actions/get-keyvault-secrets@f1125802b1ccae8c601d7c4f61ce39ea254b10c8 with: keyvault: "bitwarden-ci" secrets: "cratesio-api-token" - name: Install rust - uses: dtolnay/rust-toolchain@f361669954a8ecfc00a3443f35f9ac8e610ffc06 # stable + uses: dtolnay/rust-toolchain@439cf607258077187679211f12aa6f19af4a0af7 # stable with: toolchain: stable - name: Cache cargo registry - uses: Swatinem/rust-cache@e207df5d269b42b69c8bc5101da26f7d31feddb4 # v2.6.2 + uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43 # v2.7.0 - name: Install cargo-release run: cargo install cargo-release diff --git a/.github/workflows/release-cli.yml b/.github/workflows/release-cli.yml index 4c74d1e64..5d4d3c3e7 100644 --- a/.github/workflows/release-cli.yml +++ b/.github/workflows/release-cli.yml @@ -27,7 +27,7 @@ jobs: release-version: ${{ steps.version.outputs.version }} steps: - name: Checkout repo - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Branch check if: ${{ github.event.inputs.release_type != 'Dry Run' }} @@ -58,7 +58,7 @@ jobs: - name: Download all Release artifacts if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@f096207b7a2f31723165aee6ad03e91716686e78 + uses: bitwarden/gh-actions/download-artifacts@f1125802b1ccae8c601d7c4f61ce39ea254b10c8 with: workflow: build-cli.yml path: packages @@ -67,7 +67,7 @@ jobs: - name: Dry Run - Download all artifacts if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@f096207b7a2f31723165aee6ad03e91716686e78 + uses: bitwarden/gh-actions/download-artifacts@f1125802b1ccae8c601d7c4f61ce39ea254b10c8 with: workflow: build-cli.yml path: packages @@ -75,14 +75,14 @@ jobs: branch: master - name: Get checksum files - uses: bitwarden/gh-actions/get-checksum@f096207b7a2f31723165aee6ad03e91716686e78 + uses: bitwarden/gh-actions/get-checksum@f1125802b1ccae8c601d7c4f61ce39ea254b10c8 with: packages_dir: "packages" file_path: "packages/bws-sha256-checksums-${{ steps.version.outputs.version }}.txt" - name: Create release if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: ncipollo/release-action@a2e71bdd4e7dab70ca26a852f29600c98b33153e # v1.12.0 + uses: ncipollo/release-action@6c75be85e571768fa31b40abf38de58ba0397db5 # v1.13.0 env: PKG_VERSION: ${{ steps.version.outputs.version }} with: @@ -125,7 +125,7 @@ jobs: - setup steps: - name: Checkout - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Login to Azure uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 @@ -134,18 +134,18 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@f096207b7a2f31723165aee6ad03e91716686e78 + uses: bitwarden/gh-actions/get-keyvault-secrets@f1125802b1ccae8c601d7c4f61ce39ea254b10c8 with: keyvault: "bitwarden-ci" secrets: "cratesio-api-token" - name: Install rust - uses: dtolnay/rust-toolchain@f361669954a8ecfc00a3443f35f9ac8e610ffc06 # stable + uses: dtolnay/rust-toolchain@439cf607258077187679211f12aa6f19af4a0af7 # stable with: toolchain: stable - name: Cache cargo registry - uses: Swatinem/rust-cache@e207df5d269b42b69c8bc5101da26f7d31feddb4 # v2.6.2 + uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43 # v2.7.0 - name: Install cargo-release run: cargo install cargo-release diff --git a/.github/workflows/release-napi.yml b/.github/workflows/release-napi.yml index e5c652fb9..5c4992fc5 100644 --- a/.github/workflows/release-napi.yml +++ b/.github/workflows/release-napi.yml @@ -33,7 +33,7 @@ jobs: release-version: ${{ steps.version.outputs.version }} steps: - name: Checkout repo - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Branch check if: ${{ github.event.inputs.release_type != 'Dry Run' }} @@ -47,7 +47,7 @@ jobs: - name: Check Release Version id: version - uses: bitwarden/gh-actions/release-version-check@f096207b7a2f31723165aee6ad03e91716686e78 + uses: bitwarden/gh-actions/release-version-check@f1125802b1ccae8c601d7c4f61ce39ea254b10c8 with: release-type: ${{ github.event.inputs.release_type }} project-type: ts @@ -90,7 +90,7 @@ jobs: _PKG_VERSION: ${{ needs.setup.outputs.release-version }} steps: - name: Checkout repo - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Setup Node uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1 @@ -101,7 +101,7 @@ jobs: - name: Download schemas if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@f096207b7a2f31723165aee6ad03e91716686e78 + uses: bitwarden/gh-actions/download-artifacts@f1125802b1ccae8c601d7c4f61ce39ea254b10c8 with: workflow: build-napi.yml artifacts: schemas.ts @@ -111,7 +111,7 @@ jobs: - name: Dry Run - Download schemas if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@f096207b7a2f31723165aee6ad03e91716686e78 + uses: bitwarden/gh-actions/download-artifacts@f1125802b1ccae8c601d7c4f61ce39ea254b10c8 with: workflow: build-napi.yml artifacts: schemas.ts @@ -132,14 +132,14 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@f096207b7a2f31723165aee6ad03e91716686e78 + uses: bitwarden/gh-actions/get-keyvault-secrets@f1125802b1ccae8c601d7c4f61ce39ea254b10c8 with: keyvault: "bitwarden-ci" secrets: "npm-api-key" - name: Download artifacts if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@f096207b7a2f31723165aee6ad03e91716686e78 + uses: bitwarden/gh-actions/download-artifacts@f1125802b1ccae8c601d7c4f61ce39ea254b10c8 with: workflow: build-napi.yml path: ${{ github.workspace }}/crates/bitwarden-napi/artifacts @@ -148,7 +148,7 @@ jobs: - name: Dry Run - Download artifacts if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@f096207b7a2f31723165aee6ad03e91716686e78 + uses: bitwarden/gh-actions/download-artifacts@f1125802b1ccae8c601d7c4f61ce39ea254b10c8 with: workflow: build-napi.yml path: ${{ github.workspace }}/crates/bitwarden-napi/artifacts diff --git a/.github/workflows/rust-test.yml b/.github/workflows/rust-test.yml index 9cc44a962..f77cb29f6 100644 --- a/.github/workflows/rust-test.yml +++ b/.github/workflows/rust-test.yml @@ -36,15 +36,15 @@ jobs: steps: - name: Checkout - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Install rust - uses: dtolnay/rust-toolchain@f361669954a8ecfc00a3443f35f9ac8e610ffc06 # stable + uses: dtolnay/rust-toolchain@439cf607258077187679211f12aa6f19af4a0af7 # stable with: toolchain: stable - name: Cache cargo registry - uses: Swatinem/rust-cache@e207df5d269b42b69c8bc5101da26f7d31feddb4 # v2.6.2 + uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43 # v2.7.0 - name: Test run: cargo test --all-features @@ -56,16 +56,16 @@ jobs: steps: - name: Checkout - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Install rust - uses: dtolnay/rust-toolchain@f361669954a8ecfc00a3443f35f9ac8e610ffc06 # stable + uses: dtolnay/rust-toolchain@439cf607258077187679211f12aa6f19af4a0af7 # stable with: toolchain: stable targets: wasm32-unknown-unknown - name: Cache cargo registry - uses: Swatinem/rust-cache@e207df5d269b42b69c8bc5101da26f7d31feddb4 # v2.6.2 + uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43 # v2.7.0 - name: Check run: cargo check -p bitwarden-wasm --target wasm32-unknown-unknown diff --git a/.github/workflows/version-bump.yml b/.github/workflows/version-bump.yml index d370d5e6a..c672edb61 100644 --- a/.github/workflows/version-bump.yml +++ b/.github/workflows/version-bump.yml @@ -29,15 +29,15 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout Branch - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Install rust - uses: dtolnay/rust-toolchain@f361669954a8ecfc00a3443f35f9ac8e610ffc06 # stable + uses: dtolnay/rust-toolchain@439cf607258077187679211f12aa6f19af4a0af7 # stable with: toolchain: stable - name: Cache cargo registry - uses: Swatinem/rust-cache@e207df5d269b42b69c8bc5101da26f7d31feddb4 # v2.6.2 + uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43 # v2.7.0 - name: Install cargo-release run: cargo install cargo-edit @@ -49,13 +49,13 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@f096207b7a2f31723165aee6ad03e91716686e78 + uses: bitwarden/gh-actions/get-keyvault-secrets@f1125802b1ccae8c601d7c4f61ce39ea254b10c8 with: keyvault: "bitwarden-ci" secrets: "github-gpg-private-key, github-gpg-private-key-passphrase" - name: Import GPG key - uses: crazy-max/ghaction-import-gpg@72b6676b71ab476b77e676928516f6982eef7a41 # v5.3.0 + uses: crazy-max/ghaction-import-gpg@82a020f1f7f605c65dd2449b392a52c3fcfef7ef # v6.0.0 with: gpg_private_key: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key }} passphrase: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key-passphrase }} diff --git a/.gitignore b/.gitignore index 9e450f9ab..3f459493b 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,6 @@ languages/kotlin/sdk/src/main/java/com/bitwarden/core/bitwarden.kt # Schemas support/schemas crates/bitwarden-napi/src-ts/bitwarden_client/schemas.ts -languages/csharp/schemas.cs +languages/csharp/Bitwarden.Sdk/schemas.cs languages/js_webassembly/bitwarden_client/schemas.ts languages/python/BitwardenClient/schemas.py diff --git a/.prettierignore b/.prettierignore index ba98edfb6..d5ffe5a0e 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,5 +1,8 @@ target -languages +languages/* +!/languages/kotlin +languages/kotlin/* +!/languages/kotlin/doc.md schemas /crates/bitwarden-napi/src-ts/bitwarden_client/schemas.ts about.hbs diff --git a/.prettierrc.json b/.prettierrc.json index de753c537..af82b68c4 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,3 +1,11 @@ { - "printWidth": 100 + "printWidth": 100, + "overrides": [ + { + "files": "*.md", + "options": { + "proseWrap": "always" + } + } + ] } diff --git a/.vscode/launch.json b/.vscode/launch.json index cb99bc8e0..7da474a22 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "buildCsharp", - "program": "${workspaceFolder}/languages/csharp/bin/Debug/net6.0/bitwardenSdk.dll", + "program": "${workspaceFolder}/languages/csharp/Bitwarden.Sdk/bin/Debug/net6.0/BitwardenSdk.dll", "args": [], "env": { "RUST_LOG": "debug" diff --git a/.vscode/settings.json b/.vscode/settings.json index ab645ffae..e75498e9c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,7 @@ { "cSpell.words": [ "bindgen", + "Bitwarden", "Cdecl", "chrono", "cloc", @@ -12,6 +13,8 @@ "Maybeable", "Oaep", "Pbkdf", + "PKCS8", + "repr", "schemars", "uniffi", "wordlist" diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 491d770c9..55dc10eb2 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -30,7 +30,7 @@ "type": "process", "args": [ "build", - "${workspaceFolder}/languages/csharp/bitwardenSdk.csproj", + "${workspaceFolder}/languages/csharp/Bitwarden.Sdk/Bitwarden.Sdk.csproj", "/property:GenerateFullPaths=true", "/consoleloggerparameters:NoSummary" ], diff --git a/Cargo.lock b/Cargo.lock index 12c0395d1..02ec5a326 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] @@ -30,9 +30,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.0.4" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" +checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" dependencies = [ "memchr", ] @@ -69,24 +69,23 @@ dependencies = [ [[package]] name = "anstream" -version = "0.3.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is-terminal", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" +checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46" [[package]] name = "anstyle-parse" @@ -108,9 +107,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "1.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c" +checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" dependencies = [ "anstyle", "windows-sys 0.48.0", @@ -130,9 +129,9 @@ checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" [[package]] name = "argon2" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2e554a8638bdc1e4eae9984845306cc95f8a9208ba8d49c3859fd958b46774d" +checksum = "17ba4cac0a46bc1d2912652a751c47f2a9f3a7fe89bcae2275d418f5270402f9" dependencies = [ "base64ct", "blake2", @@ -163,7 +162,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.29", + "syn 2.0.37", ] [[package]] @@ -216,7 +215,7 @@ checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.37", ] [[package]] @@ -227,9 +226,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", @@ -248,9 +247,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.2" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" [[package]] name = "base64ct" @@ -323,7 +322,7 @@ dependencies = [ "aes", "argon2", "assert_matches", - "base64 0.21.2", + "base64 0.21.4", "bitwarden-api-api", "bitwarden-api-identity", "cbc", @@ -395,6 +394,7 @@ version = "0.1.0" dependencies = [ "clap", "color-eyre", + "inquire", "supports-color", ] @@ -489,9 +489,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.6.0" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" +checksum = "4c2f7349907b712260e64b0afe2f84692af14a454be26187d9df565c7f69266a" dependencies = [ "memchr", "serde", @@ -499,9 +499,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "bw" @@ -540,15 +540,15 @@ dependencies = [ "tempfile", "thiserror", "tokio", - "toml 0.7.6", + "toml 0.8.0", "uuid", ] [[package]] name = "bytemuck" -version = "1.13.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" +checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" [[package]] name = "byteorder" @@ -558,15 +558,15 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "bytesize" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38fcc2979eff34a4b84e1cf9a1e3da42a7d44b3b690a40cdcb23e3d556cfb2e5" +checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" [[package]] name = "camino" @@ -632,15 +632,15 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.26" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", "serde", - "winapi", + "windows-targets 0.48.5", ] [[package]] @@ -682,54 +682,52 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.23" +version = "4.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03aef18ddf7d879c15ce20f04826ef8418101c7e528014c3eeea13321047dca3" +checksum = "b1d7b8d5ec32af0fadc644bf1fd509a688c2103b185644bb1e29d164e0703136" dependencies = [ "clap_builder", "clap_derive", - "once_cell", ] [[package]] name = "clap_builder" -version = "4.3.23" +version = "4.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ce6fffb678c9b80a70b6b6de0aad31df727623a70fd9a842c30cd573e2fa98" +checksum = "5179bb514e4d7c2051749d8fcefa2ed6d06a9f4e6d69faf3805f5d80b8cf8d56" dependencies = [ "anstream", "anstyle", "clap_lex", - "once_cell", "strsim", ] [[package]] name = "clap_complete" -version = "4.3.2" +version = "4.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc443334c81a804575546c5a8a79b4913b50e28d69232903604cada1de817ce" +checksum = "4110a1e6af615a9e6d0a36f805d5c99099f8bab9b8042f5bc1fa220a4a89e36f" dependencies = [ "clap", ] [[package]] name = "clap_derive" -version = "4.3.12" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" +checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.37", ] [[package]] name = "clap_lex" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" [[package]] name = "clircle" @@ -1016,7 +1014,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f34ba9a9bcb8645379e9de8cb3ecfcf4d1c85ba66d90deb3259206fa5aa193b" dependencies = [ "quote", - "syn 2.0.29", + "syn 2.0.37", ] [[package]] @@ -1099,9 +1097,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfc4744c1b8f2a09adc0e55242f60b1af195d88596bd8700be74418c056c555" +checksum = "23d2f3407d9a573d666de4b5bdf10569d73ca9478087346697dcbae6244bfbcd" [[package]] name = "either" @@ -1181,9 +1179,9 @@ checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" [[package]] name = "encoding_rs" -version = "0.8.32" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ "cfg-if", ] @@ -1209,9 +1207,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" +checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" dependencies = [ "errno-dragonfly", "libc", @@ -1376,7 +1374,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.37", ] [[package]] @@ -1451,9 +1449,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.27.3" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" [[package]] name = "glob" @@ -1487,9 +1485,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.20" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" dependencies = [ "bytes", "fnv", @@ -1530,9 +1528,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "hkdf" @@ -1824,9 +1822,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.147" +version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "libloading" @@ -1861,9 +1859,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" +checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" [[package]] name = "lock_api" @@ -1883,9 +1881,9 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" [[package]] name = "memoffset" @@ -2045,9 +2043,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ "autocfg", "num-integer", @@ -2114,9 +2112,9 @@ dependencies = [ [[package]] name = "object" -version = "0.31.1" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" dependencies = [ "memchr", ] @@ -2157,11 +2155,11 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "openssl" -version = "0.10.56" +version = "0.10.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "729b745ad4a5575dd06a3e1af1414bd330ee561c01b3899eb584baeaa8def17e" +checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "cfg-if", "foreign-types", "libc", @@ -2178,7 +2176,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.37", ] [[package]] @@ -2189,18 +2187,18 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "111.27.0+1.1.1v" +version = "300.1.5+3.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e8f197c82d7511c5b014030c9b1efeda40d7d5f99d23b4ceed3524a5e63f02" +checksum = "559068e4c12950d7dcaa1857a61725c0d38d4fc03ff8e070ab31a75d6e316491" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.91" +version = "0.9.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac" +checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" dependencies = [ "cc", "libc", @@ -2302,9 +2300,9 @@ checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project-lite" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -2351,7 +2349,7 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdc0001cfea3db57a2e24bc0d818e9e20e554b5f97fabb9bc231dc240269ae06" dependencies = [ - "base64 0.21.2", + "base64 0.21.4", "indexmap 1.9.3", "line-wrap", "quick-xml", @@ -2395,9 +2393,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] @@ -2638,9 +2636,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.3" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" +checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" dependencies = [ "aho-corasick", "memchr", @@ -2650,9 +2648,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" +checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" dependencies = [ "aho-corasick", "memchr", @@ -2661,17 +2659,17 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "reqwest" -version = "0.11.18" +version = "0.11.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" +checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" dependencies = [ - "base64 0.21.2", + "base64 0.21.4", "bytes", "encoding_rs", "futures-core", @@ -2748,9 +2746,9 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.8" +version = "0.38.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" +checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" dependencies = [ "bitflags 2.4.0", "errno", @@ -2797,9 +2795,9 @@ dependencies = [ [[package]] name = "schemars" -version = "0.8.12" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" +checksum = "1f7b0ce13155372a76ee2e1c5ffba1fe61ede73fbea5630d61eee6fac4929c0c" dependencies = [ "chrono", "dyn-clone", @@ -2812,9 +2810,9 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.12" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c" +checksum = "e85e2a16b12bdb763244c69ab79363d71db2b4b918a2def53f80b02e0574b13c" dependencies = [ "proc-macro2", "quote", @@ -2851,7 +2849,7 @@ checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.37", ] [[package]] @@ -2892,31 +2890,31 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.185" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be9b6f69f1dfd54c3b568ffa45c310d6973a5e5148fd40cf515acaf38cf5bc31" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.185" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc59dfdcbad1437773485e0367fea4b090a2e0a16d9ffc46af47764536a298ec" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.37", ] [[package]] @@ -2932,9 +2930,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.105" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", @@ -2971,7 +2969,7 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.37", ] [[package]] @@ -3022,9 +3020,9 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", @@ -3093,24 +3091,24 @@ dependencies = [ [[package]] name = "siphasher" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "slab" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "socket2" @@ -3124,9 +3122,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" dependencies = [ "libc", "windows-sys 0.48.0", @@ -3193,9 +3191,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "supports-color" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4950e7174bffabe99455511c39707310e7e9b440364a2fcb1cc21521be57b354" +checksum = "d6398cde53adc3c4557306a96ce67b302968513830a77a95b2b17305d9719a89" dependencies = [ "is-terminal", "is_ci", @@ -3214,9 +3212,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.29" +version = "2.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" dependencies = [ "proc-macro2", "quote", @@ -3263,31 +3261,31 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.47" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" +checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.47" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" +checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.37", ] [[package]] @@ -3302,9 +3300,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.25" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fdd63d58b18d663fbdf70e049f00a22c8e42be082203be7f26589213cd75ea" +checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" dependencies = [ "deranged", "itoa", @@ -3315,15 +3313,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.11" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb71511c991639bb078fd5bf97757e03914361c48100d52878b8e52b46fb92cd" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" dependencies = [ "time-core", ] @@ -3365,7 +3363,7 @@ dependencies = [ "mio", "num_cpus", "pin-project-lite", - "socket2 0.5.3", + "socket2 0.5.4", "tokio-macros", "windows-sys 0.48.0", ] @@ -3378,7 +3376,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.37", ] [[package]] @@ -3393,9 +3391,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" dependencies = [ "bytes", "futures-core", @@ -3416,9 +3414,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.6" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" +checksum = "c226a7bba6d859b63c92c4b4fe69c5b6b72d0cb897dbc8e6012298e6154cb56e" dependencies = [ "serde", "serde_spanned", @@ -3437,9 +3435,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.19.14" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +checksum = "8ff63e60a958cefbb518ae1fd6566af80d9d4be430a33f3723dfc47d1d411d95" dependencies = [ "indexmap 2.0.0", "serde", @@ -3504,15 +3502,15 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicase" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" dependencies = [ "version_check", ] @@ -3525,9 +3523,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" @@ -3546,9 +3544,9 @@ checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unicode-width" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "uniffi" @@ -3611,7 +3609,7 @@ version = "0.24.1" source = "git+https://github.com/mozilla/uniffi-rs?rev=53d5ac7274d8b4d66ad35b68cb6e2d89898f96af#53d5ac7274d8b4d66ad35b68cb6e2d89898f96af" dependencies = [ "quote", - "syn 2.0.29", + "syn 2.0.37", ] [[package]] @@ -3641,7 +3639,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.29", + "syn 2.0.37", "toml 0.5.11", "uniffi_build", "uniffi_meta", @@ -3698,9 +3696,9 @@ checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" [[package]] name = "url" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" dependencies = [ "form_urlencoded", "idna", @@ -3749,9 +3747,9 @@ checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" [[package]] name = "walkdir" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" dependencies = [ "same-file", "winapi-util", @@ -3801,7 +3799,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.37", "wasm-bindgen-shared", ] @@ -3835,7 +3833,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.37", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3906,9 +3904,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] @@ -4062,20 +4060,21 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d09770118a7eb1ccaf4a594a221334119a44a814fcb0d31c5b85e83e97227a97" +checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" dependencies = [ "memchr", ] [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi", + "cfg-if", + "windows-sys 0.48.0", ] [[package]] @@ -4086,7 +4085,7 @@ checksum = "c6f71803d3a1c80377a06221e0530be02035d5b3e854af56c6ece7ac20ac441d" dependencies = [ "assert-json-diff", "async-trait", - "base64 0.21.2", + "base64 0.21.4", "deadpool", "futures", "futures-timer", diff --git a/README.md b/README.md index 9c3d3fe68..18613d4dc 100644 --- a/README.md +++ b/README.md @@ -20,10 +20,13 @@ cargo build The project is structured as a monorepo using cargo workspaces. - [`bitwarden`](./crates/bitwarden/): Rust friendly API for interacting with the secrets manager. -- [`bitwarden-api-api`](./crates/bitwarden-api-api/): Auto-generated API bindings for the API server. -- [`bitwarden-api-identity`](./crates/bitwarden-api-identity/): Auto-generated API bindings for the Identity server. +- [`bitwarden-api-api`](./crates/bitwarden-api-api/): Auto-generated API bindings for the API + server. +- [`bitwarden-api-identity`](./crates/bitwarden-api-identity/): Auto-generated API bindings for the + Identity server. - [`bitwarden-c`](./crates/bitwarden-c/): C bindings for FFI interop. -- [`bitwarden-json`](./crates/bitwarden-json/): JSON wrapper around the `bitwarden` crate. Powers the other language bindings. +- [`bitwarden-json`](./crates/bitwarden-json/): JSON wrapper around the `bitwarden` crate. Powers + the other language bindings. - [`bitwarden-napi`](./crates/bitwarden-napi/): Node-API bindings. - [`bws`](./crates/bws/): CLI for interacting with the secrets manager. - [`sdk-schemas`](./crates/sdk-schemas/): Generator for the _json schemas_. @@ -34,9 +37,9 @@ To minimize the amount of work required to support additional bindings the proje around a `json` based API. With every binding only needing to implement one method, namely `run_command`. -To ensure type safety in the API, _json schemas_ are generated from the rust structs in `bitwarden` using [schemars](https://crates.io/crates/schemars). -The _json schemas_ are later used to generate the API bindings for each language using -[QuickType](https://github.com/quicktype/quicktype). +To ensure type safety in the API, _json schemas_ are generated from the rust structs in `bitwarden` +using [schemars](https://crates.io/crates/schemars). The _json schemas_ are later used to generate +the API bindings for each language using [QuickType](https://github.com/quicktype/quicktype). ```bash npm run schemas @@ -44,8 +47,9 @@ npm run schemas ## API Bindings -We autogenerate the server bindings using [openapi-generator](https://github.com/OpenAPITools/openapi-generator). -To do this we first need to build the internal swagger documentation. +We autogenerate the server bindings using +[openapi-generator](https://github.com/OpenAPITools/openapi-generator). To do this we first need to +build the internal swagger documentation. ### Swagger generation @@ -81,7 +85,8 @@ npx openapi-generator-cli generate ` --additional-properties=packageVersion=1.0.0 ``` -OpenApi Generator works using templates, we have customized our templates to work better with our codebase. +OpenApi Generator works using templates, we have customized our templates to work better with our +codebase. - https://github.com/OpenAPITools/openapi-generator/issues/10977 - https://github.com/OpenAPITools/openapi-generator/issues/12464 @@ -90,4 +95,5 @@ There is also a scenario where we have a negative integer enum which completely generation. In that case we excluded the file from being generated and manually patched it. `crates/bitwarden-api-api/src/models/organization_user_status_type.rs` -The hope going forward is that we can continue to use the generator with minimal manual intervention. +The hope going forward is that we can continue to use the generator with minimal manual +intervention. diff --git a/crates/bitwarden-api-api/README.md b/crates/bitwarden-api-api/README.md index 0754e9680..255041dc5 100644 --- a/crates/bitwarden-api-api/README.md +++ b/crates/bitwarden-api-api/README.md @@ -1,10 +1,13 @@ # Rust API client for bitwarden-api-api -No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) +No description provided (generated by Openapi Generator +https://github.com/openapitools/openapi-generator) ## Overview -This API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the [openapi-spec](https://openapis.org) from a remote server, you can easily generate an API client. +This API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By +using the [openapi-spec](https://openapis.org) from a remote server, you can easily generate an API +client. - API version: latest - Package version: 1.0.0 @@ -12,7 +15,8 @@ This API client was generated by the [OpenAPI Generator](https://openapi-generat ## Installation -Put the package under your project folder in a directory named `bitwarden-api-api` and add the following to `Cargo.toml` under `[dependencies]`: +Put the package under your project folder in a directory named `bitwarden-api-api` and add the +following to `Cargo.toml` under `[dependencies]`: ``` bitwarden-api-api = { path = "./bitwarden-api-api" } diff --git a/crates/bitwarden-api-identity/README.md b/crates/bitwarden-api-identity/README.md index ba71ced56..8288a18e5 100644 --- a/crates/bitwarden-api-identity/README.md +++ b/crates/bitwarden-api-identity/README.md @@ -1,10 +1,13 @@ # Rust API client for bitwarden-api-identity -No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) +No description provided (generated by Openapi Generator +https://github.com/openapitools/openapi-generator) ## Overview -This API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the [openapi-spec](https://openapis.org) from a remote server, you can easily generate an API client. +This API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By +using the [openapi-spec](https://openapis.org) from a remote server, you can easily generate an API +client. - API version: v1 - Package version: 1.0.0 @@ -12,7 +15,8 @@ This API client was generated by the [OpenAPI Generator](https://openapi-generat ## Installation -Put the package under your project folder in a directory named `bitwarden-api-identity` and add the following to `Cargo.toml` under `[dependencies]`: +Put the package under your project folder in a directory named `bitwarden-api-identity` and add the +following to `Cargo.toml` under `[dependencies]`: ``` bitwarden-api-identity = { path = "./bitwarden-api-identity" } diff --git a/crates/bitwarden-cli/Cargo.toml b/crates/bitwarden-cli/Cargo.toml index ca2e4ba7c..606e24856 100644 --- a/crates/bitwarden-cli/Cargo.toml +++ b/crates/bitwarden-cli/Cargo.toml @@ -7,4 +7,5 @@ rust-version = "1.57" [dependencies] clap = { version = "4.3.0", features = ["derive"] } color-eyre = "0.6" +inquire = "0.6.2" supports-color = "2.0.0" diff --git a/crates/bitwarden-cli/src/lib.rs b/crates/bitwarden-cli/src/lib.rs index 2652553f0..d0c6a836e 100644 --- a/crates/bitwarden-cli/src/lib.rs +++ b/crates/bitwarden-cli/src/lib.rs @@ -1,3 +1,15 @@ mod color; pub use color::{install_color_eyre, Color}; +use inquire::{error::InquireResult, Text}; + +/// Prompt the user for input if the value is None +/// +/// Typically used when the user can provide a value via CLI or prompt +pub fn text_prompt_when_none(prompt: &str, val: Option) -> InquireResult { + Ok(if let Some(val) = val { + val + } else { + Text::new(prompt).prompt()? + }) +} diff --git a/crates/bitwarden-napi/package-lock.json b/crates/bitwarden-napi/package-lock.json index 1a1b31054..02ea37bec 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.5.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.1.tgz", - "integrity": "sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg==", + "version": "20.6.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.5.tgz", + "integrity": "sha512-2qGq5LAOTh9izcc0+F+dToFigBWiK1phKPt7rNhOqJSr35y8rlIBjDwGtFSgAI6MGIhjwOVNSQZVdJsZJ2uR1w==", "dev": true, "peer": true }, @@ -193,9 +193,9 @@ } }, "node_modules/typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true, "bin": { "tsc": "bin/tsc", diff --git a/crates/bitwarden-napi/src-ts/bitwarden_client/index.ts b/crates/bitwarden-napi/src-ts/bitwarden_client/index.ts index 9f48fe938..eefbb1204 100644 --- a/crates/bitwarden-napi/src-ts/bitwarden_client/index.ts +++ b/crates/bitwarden-napi/src-ts/bitwarden_client/index.ts @@ -4,7 +4,6 @@ import { ClientSettings, Convert, ResponseForAPIKeyLoginResponse, - ResponseForPasswordLoginResponse, ResponseForSecretIdentifiersResponse, ResponseForSecretResponse, ResponseForSecretsDeleteResponse, @@ -19,19 +18,6 @@ export class BitwardenClient { this.client = new rust.BitwardenClient(settingsJson, loggingLevel ?? LogLevel.Info); } - async login(email: string, password: string): Promise { - const response = await this.client.runCommand( - Convert.commandToJson({ - passwordLogin: { - email: email, - password: password, - }, - }), - ); - - return Convert.toResponseForPasswordLoginResponse(response); - } - async loginWithAccessToken(accessToken: string): Promise { const commandInput = Convert.commandToJson({ accessTokenLogin: { diff --git a/crates/bitwarden-uniffi/src/auth/mod.rs b/crates/bitwarden-uniffi/src/auth/mod.rs index fed3dc058..a5107768e 100644 --- a/crates/bitwarden-uniffi/src/auth/mod.rs +++ b/crates/bitwarden-uniffi/src/auth/mod.rs @@ -1,6 +1,9 @@ use std::sync::Arc; -use bitwarden::{auth::password::MasterPasswordPolicyOptions, client::auth_settings::Kdf}; +use bitwarden::{ + auth::{password::MasterPasswordPolicyOptions, RegisterKeyResponse}, + client::kdf::Kdf, +}; use crate::{error::Result, Client}; @@ -18,7 +21,7 @@ impl ClientAuth { ) -> u8 { self.0 .0 - .read() + .write() .await .auth() .password_strength(password, email, additional_inputs) @@ -34,7 +37,7 @@ impl ClientAuth { ) -> bool { self.0 .0 - .read() + .write() .await .auth() .satisfies_policy(password, strength, &policy) @@ -57,4 +60,20 @@ impl ClientAuth { .hash_password(email, password, kdf_params) .await?) } + + /// Generate keys needed for registration process + pub async fn make_register_keys( + &self, + email: String, + password: String, + kdf: Kdf, + ) -> Result { + Ok(self + .0 + .0 + .write() + .await + .auth() + .make_register_keys(email, password, kdf)?) + } } diff --git a/crates/bitwarden-uniffi/src/docs.rs b/crates/bitwarden-uniffi/src/docs.rs index f67e6fb98..bd30e974e 100644 --- a/crates/bitwarden-uniffi/src/docs.rs +++ b/crates/bitwarden-uniffi/src/docs.rs @@ -1,9 +1,9 @@ use bitwarden::{ auth::password::MasterPasswordPolicyOptions, - client::auth_settings::Kdf, + client::kdf::Kdf, mobile::crypto::InitCryptoRequest, - tool::{PassphraseGeneratorRequest, PasswordGeneratorRequest}, - vault::{Cipher, CipherView, Collection, Folder, FolderView}, + tool::{ExportFormat, PassphraseGeneratorRequest, PasswordGeneratorRequest}, + vault::{Cipher, CipherView, Collection, Folder, FolderView, Send, SendListView, SendView}, }; use schemars::JsonSchema; @@ -16,6 +16,9 @@ pub enum DocRef { Collection(Collection), Folder(Folder), FolderView(FolderView), + Send(Send), + SendView(SendView), + SendListView(SendListView), // Crypto InitCryptoRequest(InitCryptoRequest), @@ -24,6 +27,9 @@ pub enum DocRef { PasswordGeneratorRequest(PasswordGeneratorRequest), PassphraseGeneratorRequest(PassphraseGeneratorRequest), + // Exporters + ExportFormat(ExportFormat), + // Auth MasterPasswordPolicyOptions(MasterPasswordPolicyOptions), diff --git a/crates/bitwarden-uniffi/src/tool/mod.rs b/crates/bitwarden-uniffi/src/tool/mod.rs index 3f4faed45..3243cceb6 100644 --- a/crates/bitwarden-uniffi/src/tool/mod.rs +++ b/crates/bitwarden-uniffi/src/tool/mod.rs @@ -1,6 +1,9 @@ use std::sync::Arc; -use bitwarden::tool::{PassphraseGeneratorRequest, PasswordGeneratorRequest}; +use bitwarden::{ + tool::{ExportFormat, PassphraseGeneratorRequest, PasswordGeneratorRequest}, + vault::{Cipher, Collection, Folder}, +}; use crate::{error::Result, Client}; @@ -33,3 +36,43 @@ impl ClientGenerators { .await?) } } + +#[derive(uniffi::Object)] +pub struct ClientExporters(pub(crate) Arc); + +#[uniffi::export] +impl ClientExporters { + /// **API Draft:** Export user vault + pub async fn export_vault( + &self, + folders: Vec, + ciphers: Vec, + format: ExportFormat, + ) -> Result { + Ok(self + .0 + .0 + .read() + .await + .exporters() + .export_vault(folders, ciphers, format) + .await?) + } + + /// **API Draft:** Export organization vault + pub async fn export_organization_vault( + &self, + collections: Vec, + ciphers: Vec, + format: ExportFormat, + ) -> Result { + Ok(self + .0 + .0 + .read() + .await + .exporters() + .export_organization_vault(collections, ciphers, format) + .await?) + } +} diff --git a/crates/bitwarden-uniffi/src/vault/mod.rs b/crates/bitwarden-uniffi/src/vault/mod.rs index 53567c063..2692632ab 100644 --- a/crates/bitwarden-uniffi/src/vault/mod.rs +++ b/crates/bitwarden-uniffi/src/vault/mod.rs @@ -6,6 +6,7 @@ pub mod ciphers; pub mod collections; pub mod folders; pub mod password_history; +pub mod sends; #[derive(uniffi::Object)] pub struct ClientVault(pub(crate) Arc); @@ -27,8 +28,13 @@ impl ClientVault { Arc::new(ciphers::ClientCiphers(self.0.clone())) } - /// Ciphers operations + /// Password history operations pub fn password_history(self: Arc) -> Arc { Arc::new(password_history::ClientPasswordHistory(self.0.clone())) } + + /// Sends operations + pub fn sends(self: Arc) -> Arc { + Arc::new(sends::ClientSends(self.0.clone())) + } } diff --git a/crates/bitwarden-uniffi/src/vault/sends.rs b/crates/bitwarden-uniffi/src/vault/sends.rs new file mode 100644 index 000000000..6e2f1b879 --- /dev/null +++ b/crates/bitwarden-uniffi/src/vault/sends.rs @@ -0,0 +1,104 @@ +use std::{path::Path, sync::Arc}; + +use bitwarden::vault::{Send, SendListView, SendView}; + +use crate::{Client, Result}; + +#[derive(uniffi::Object)] +pub struct ClientSends(pub Arc); + +#[uniffi::export] +impl ClientSends { + /// Encrypt send + pub async fn encrypt(&self, send: SendView) -> Result { + Ok(self.0 .0.read().await.vault().sends().encrypt(send).await?) + } + + /// Encrypt a send file in memory + pub async fn encrypt_buffer(&self, send: Send, buffer: Vec) -> Result> { + Ok(self + .0 + .0 + .read() + .await + .vault() + .sends() + .encrypt_buffer(send, &buffer) + .await?) + } + + /// Encrypt a send file located in the file system + pub async fn encrypt_file( + &self, + send: Send, + decrypted_file_path: String, + encrypted_file_path: String, + ) -> Result<()> { + Ok(self + .0 + .0 + .read() + .await + .vault() + .sends() + .encrypt_file( + send, + Path::new(&decrypted_file_path), + Path::new(&encrypted_file_path), + ) + .await?) + } + + /// Decrypt send + pub async fn decrypt(&self, send: Send) -> Result { + Ok(self.0 .0.read().await.vault().sends().decrypt(send).await?) + } + + /// Decrypt send list + pub async fn decrypt_list(&self, sends: Vec) -> Result> { + Ok(self + .0 + .0 + .read() + .await + .vault() + .sends() + .decrypt_list(sends) + .await?) + } + + /// Decrypt a send file in memory + pub async fn decrypt_buffer(&self, send: Send, buffer: Vec) -> Result> { + Ok(self + .0 + .0 + .read() + .await + .vault() + .sends() + .decrypt_buffer(send, &buffer) + .await?) + } + + /// Decrypt a send file located in the file system + pub async fn decrypt_file( + &self, + send: Send, + encrypted_file_path: String, + decrypted_file_path: String, + ) -> Result<()> { + Ok(self + .0 + .0 + .read() + .await + .vault() + .sends() + .decrypt_file( + send, + Path::new(&encrypted_file_path), + Path::new(&decrypted_file_path), + ) + .await?) + } +} diff --git a/crates/bitwarden/CHANGELOG.md b/crates/bitwarden/CHANGELOG.md index 4c99ad091..d61af5206 100644 --- a/crates/bitwarden/CHANGELOG.md +++ b/crates/bitwarden/CHANGELOG.md @@ -2,8 +2,8 @@ All notable changes to this project will be documented in this file. -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project +adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] @@ -17,8 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Deprecated -- The secrets manager SDK is now hidden behind a `secrets` feature flag. Make sure to enable - this flag in your `Cargo.toml` file. At the moment the flag is enabled by default for compatibility +- The secrets manager SDK is now hidden behind a `secrets` feature flag. Make sure to enable this + flag in your `Cargo.toml` file. At the moment the flag is enabled by default for compatibility reasons, but this is considered deprecated and the flag will be made opt-in eventually. ### Added diff --git a/crates/bitwarden/README.md b/crates/bitwarden/README.md index ef024e06b..a0708b7e6 100644 --- a/crates/bitwarden/README.md +++ b/crates/bitwarden/README.md @@ -1,8 +1,8 @@ # Bitwarden Secrets Manager SDK A Rust client SDK to interact with the -[Bitwarden Secrets Manager](https://bitwarden.com/products/secrets-manager/). This is a beta -release and might be missing some functionality. +[Bitwarden Secrets Manager](https://bitwarden.com/products/secrets-manager/). This is a beta release +and might be missing some functionality. ## Usage diff --git a/crates/bitwarden/src/auth/client_auth.rs b/crates/bitwarden/src/auth/client_auth.rs index 086ce2752..8ffca0a44 100644 --- a/crates/bitwarden/src/auth/client_auth.rs +++ b/crates/bitwarden/src/auth/client_auth.rs @@ -1,8 +1,12 @@ -use super::password::{password_strength, satisfies_policy, MasterPasswordPolicyOptions}; -use crate::Client; +use super::{ + password::{password_strength, satisfies_policy, MasterPasswordPolicyOptions}, + register::{make_register_keys, register}, + RegisterKeyResponse, RegisterRequest, +}; +use crate::{client::kdf::Kdf, error::Result, Client}; pub struct ClientAuth<'a> { - pub(crate) _client: &'a crate::Client, + pub(crate) client: &'a mut crate::Client, } impl<'a> ClientAuth<'a> { @@ -23,10 +27,24 @@ impl<'a> ClientAuth<'a> { ) -> bool { satisfies_policy(password, strength, policy) } + + pub fn make_register_keys( + &self, + email: String, + password: String, + kdf: Kdf, + ) -> Result { + make_register_keys(email, password, kdf) + } + + #[cfg(feature = "internal")] + pub async fn register(&mut self, input: &RegisterRequest) -> Result<()> { + register(self.client, input).await + } } impl<'a> Client { - pub fn auth(&'a self) -> ClientAuth<'a> { - ClientAuth { _client: self } + pub fn auth(&'a mut self) -> ClientAuth<'a> { + ClientAuth { client: self } } } diff --git a/crates/bitwarden/src/auth/login/access_token.rs b/crates/bitwarden/src/auth/login/access_token.rs index ec3583bba..46fa35779 100644 --- a/crates/bitwarden/src/auth/login/access_token.rs +++ b/crates/bitwarden/src/auth/login/access_token.rs @@ -9,7 +9,7 @@ use crate::{ api::{request::AccessTokenRequest, response::IdentityTokenResponse}, login::{response::two_factor::TwoFactorProviders, PasswordLoginResponse}, }, - client::{AccessToken, LoginMethod}, + client::{AccessToken, LoginMethod, ServiceAccountLoginMethod}, crypto::{EncString, SymmetricCryptoKey}, error::{Error, Result}, util::{decode_token, BASE64_ENGINE}, @@ -59,11 +59,11 @@ pub(crate) async fn access_token_login( r.access_token.clone(), r.refresh_token.clone(), r.expires_in, - LoginMethod::AccessToken { + LoginMethod::ServiceAccount(ServiceAccountLoginMethod::AccessToken { service_account_id: access_token.service_account_id, client_secret: access_token.client_secret, organization_id, - }, + }), ); client.initialize_crypto_single_key(encryption_key); diff --git a/crates/bitwarden/src/auth/login/api_key.rs b/crates/bitwarden/src/auth/login/api_key.rs index 4f3167aee..a9fa954d7 100644 --- a/crates/bitwarden/src/auth/login/api_key.rs +++ b/crates/bitwarden/src/auth/login/api_key.rs @@ -6,12 +6,9 @@ use serde::{Deserialize, Serialize}; use crate::{ auth::{ api::{request::ApiTokenRequest, response::IdentityTokenResponse}, - login::{ - determine_password_hash, response::two_factor::TwoFactorProviders, - PasswordLoginResponse, - }, + login::{response::two_factor::TwoFactorProviders, PasswordLoginResponse}, }, - client::LoginMethod, + client::{LoginMethod, UserLoginMethod}, crypto::EncString, error::{Error, Result}, util::decode_token, @@ -28,16 +25,6 @@ pub(crate) async fn api_key_login( let response = request_api_identity_tokens(client, input).await?; if let IdentityTokenResponse::Authenticated(r) = &response { - client.set_tokens( - r.access_token.clone(), - r.refresh_token.clone(), - r.expires_in, - LoginMethod::ApiKey { - client_id: input.client_id.to_owned(), - client_secret: input.client_secret.to_owned(), - }, - ); - let access_token_obj = decode_token(&r.access_token)?; // This should always be Some() when logging in with an api key @@ -45,7 +32,19 @@ pub(crate) async fn api_key_login( .email .ok_or(Error::Internal("Access token doesn't contain email"))?; - let _ = determine_password_hash(client, &email, &input.password).await?; + let kdf = client.prelogin(email.clone()).await?; + + client.set_tokens( + r.access_token.clone(), + r.refresh_token.clone(), + r.expires_in, + LoginMethod::User(UserLoginMethod::ApiKey { + client_id: input.client_id.to_owned(), + client_secret: input.client_secret.to_owned(), + email, + kdf, + }), + ); let user_key = EncString::from_str(r.key.as_deref().unwrap()).unwrap(); let private_key = EncString::from_str(r.private_key.as_deref().unwrap()).unwrap(); diff --git a/crates/bitwarden/src/auth/login/mod.rs b/crates/bitwarden/src/auth/login/mod.rs index b1ab54ecf..9e1dbb818 100644 --- a/crates/bitwarden/src/auth/login/mod.rs +++ b/crates/bitwarden/src/auth/login/mod.rs @@ -1,7 +1,7 @@ #[cfg(feature = "internal")] use { crate::{ - client::{auth_settings::AuthSettings, Client}, + client::{kdf::Kdf, Client}, error::Result, }, bitwarden_api_identity::{ @@ -40,21 +40,18 @@ pub(crate) use access_token::access_token_login; pub use access_token::{AccessTokenLoginRequest, AccessTokenLoginResponse}; #[cfg(feature = "internal")] -async fn determine_password_hash( - client: &mut Client, - email: &str, - password: &str, -) -> Result { - let pre_login = request_prelogin(client, email.to_owned()).await?; - let auth_settings = AuthSettings::new(pre_login, email.to_owned()); - let password_hash = auth_settings.make_user_password_hash(password)?; - client.set_auth_settings(auth_settings); +async fn determine_password_hash(email: &str, kdf: &Kdf, password: &str) -> Result { + use crate::crypto::{HashPurpose, MasterKey}; - Ok(password_hash) + let master_key = MasterKey::derive(password.as_bytes(), email.as_bytes(), kdf)?; + master_key.derive_master_key_hash(password.as_bytes(), HashPurpose::ServerAuthorization) } #[cfg(feature = "internal")] -async fn request_prelogin(client: &mut Client, email: String) -> Result { +pub(crate) async fn request_prelogin( + client: &mut Client, + email: String, +) -> Result { let request_model = PreloginRequestModel::new(email); let config = client.get_api_configurations().await; Ok(accounts_prelogin_post(&config.identity, Some(request_model)).await?) diff --git a/crates/bitwarden/src/auth/login/password.rs b/crates/bitwarden/src/auth/login/password.rs index 0101f2ce1..33f7ea338 100644 --- a/crates/bitwarden/src/auth/login/password.rs +++ b/crates/bitwarden/src/auth/login/password.rs @@ -12,7 +12,7 @@ use crate::{ api::request::PasswordTokenRequest, login::{determine_password_hash, TwoFactorRequest}, }, - client::LoginMethod, + client::{kdf::Kdf, LoginMethod}, crypto::EncString, Client, }; @@ -29,10 +29,12 @@ pub(crate) async fn password_login( client: &mut Client, input: &PasswordLoginRequest, ) -> Result { + use crate::client::UserLoginMethod; + info!("password logging in"); debug!("{:#?}, {:#?}", client, input); - let password_hash = determine_password_hash(client, &input.email, &input.password).await?; + let password_hash = determine_password_hash(&input.email, &input.kdf, &input.password).await?; let response = request_identity_tokens(client, input, &password_hash).await?; if let IdentityTokenResponse::Authenticated(r) = &response { @@ -40,9 +42,11 @@ pub(crate) async fn password_login( r.access_token.clone(), r.refresh_token.clone(), r.expires_in, - LoginMethod::Username { + LoginMethod::User(UserLoginMethod::Username { client_id: "web".to_owned(), - }, + email: input.email.to_owned(), + kdf: input.kdf.to_owned(), + }), ); let user_key = EncString::from_str(r.key.as_deref().unwrap()).unwrap(); @@ -77,6 +81,8 @@ pub struct PasswordLoginRequest { pub password: String, // Two-factor authentication pub two_factor: Option, + /// Kdf from prelogin + pub kdf: Kdf, } #[derive(Serialize, Deserialize, Debug, JsonSchema)] diff --git a/crates/bitwarden/src/auth/login/two_factor.rs b/crates/bitwarden/src/auth/login/two_factor.rs index 6b49332e5..04c411349 100644 --- a/crates/bitwarden/src/auth/login/two_factor.rs +++ b/crates/bitwarden/src/auth/login/two_factor.rs @@ -19,7 +19,10 @@ pub(crate) async fn send_two_factor_email( client: &mut Client, input: &TwoFactorEmailRequest, ) -> Result<()> { - let password_hash = determine_password_hash(client, &input.email, &input.password).await?; + // TODO: This should be resolved from the client + let kdf = client.prelogin(input.email.clone()).await?; + + let password_hash = determine_password_hash(&input.email, &kdf, &input.password).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 28606d132..f2f6a3144 100644 --- a/crates/bitwarden/src/auth/mod.rs +++ b/crates/bitwarden/src/auth/mod.rs @@ -5,3 +5,8 @@ pub mod login; #[cfg(feature = "internal")] pub mod password; pub mod renew; + +#[cfg(feature = "internal")] +mod register; +#[cfg(feature = "internal")] +pub use register::{RegisterKeyResponse, RegisterRequest}; diff --git a/crates/bitwarden/src/auth/password.rs b/crates/bitwarden/src/auth/password.rs index a49253f6d..a38c75ab6 100644 --- a/crates/bitwarden/src/auth/password.rs +++ b/crates/bitwarden/src/auth/password.rs @@ -5,7 +5,7 @@ pub(super) fn password_strength( _email: String, _additional_inputs: Vec, ) -> u8 { - unimplemented!() + 2 } pub(super) fn satisfies_policy( @@ -13,7 +13,7 @@ pub(super) fn satisfies_policy( _strength: u8, _policy: &MasterPasswordPolicyOptions, ) -> bool { - unimplemented!() + true } #[derive(Debug, JsonSchema)] diff --git a/crates/bitwarden/src/auth/register.rs b/crates/bitwarden/src/auth/register.rs new file mode 100644 index 000000000..8336ec9c9 --- /dev/null +++ b/crates/bitwarden/src/auth/register.rs @@ -0,0 +1,85 @@ +use bitwarden_api_identity::{ + apis::accounts_api::accounts_register_post, + models::{KeysRequestModel, RegisterRequestModel}, +}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use crate::{ + client::kdf::Kdf, + crypto::{HashPurpose, MasterKey, RsaKeyPair}, + error::Result, + util::default_pbkdf2_iterations, + Client, +}; + +#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct RegisterRequest { + pub email: String, + pub name: Option, + pub password: String, + pub password_hint: Option, +} + +/// Half baked implementation of user registration +pub(super) async fn register(client: &mut Client, req: &RegisterRequest) -> Result<()> { + let config = client.get_api_configurations().await; + + let kdf = Kdf::PBKDF2 { + iterations: default_pbkdf2_iterations(), + }; + + let keys = make_register_keys(req.email.to_owned(), req.password.to_owned(), kdf)?; + + accounts_register_post( + &config.identity, + Some(RegisterRequestModel { + name: req.name.to_owned(), + email: req.email.to_owned(), + master_password_hash: keys.master_password_hash, + master_password_hint: req.password_hint.to_owned(), + captcha_response: None, // TODO: Add + key: Some(keys.encrypted_user_key.to_string()), + keys: Some(Box::new(KeysRequestModel { + public_key: Some(keys.keys.public), + encrypted_private_key: keys.keys.private.to_string(), + })), + token: None, + organization_user_id: None, + kdf: Some(bitwarden_api_identity::models::KdfType::Variant0), + kdf_iterations: Some(default_pbkdf2_iterations().get() as i32), + kdf_memory: None, + kdf_parallelism: None, + reference_data: None, // TODO: Add + }), + ) + .await?; + + Ok(()) +} + +pub(super) fn make_register_keys( + email: String, + password: String, + kdf: Kdf, +) -> Result { + let master_key = MasterKey::derive(password.as_bytes(), email.as_bytes(), &kdf)?; + let master_password_hash = + master_key.derive_master_key_hash(password.as_bytes(), HashPurpose::ServerAuthorization)?; + let (user_key, encrypted_user_key) = master_key.make_user_key()?; + let keys = user_key.make_key_pair()?; + + Ok(RegisterKeyResponse { + master_password_hash, + encrypted_user_key: encrypted_user_key.to_string(), + keys, + }) +} + +#[cfg_attr(feature = "mobile", derive(uniffi::Record))] +pub struct RegisterKeyResponse { + master_password_hash: String, + encrypted_user_key: String, + keys: RsaKeyPair, +} diff --git a/crates/bitwarden/src/auth/renew.rs b/crates/bitwarden/src/auth/renew.rs index 1752f3466..3b2dbef7d 100644 --- a/crates/bitwarden/src/auth/renew.rs +++ b/crates/bitwarden/src/auth/renew.rs @@ -1,10 +1,10 @@ use std::time::{Duration, Instant}; #[cfg(feature = "internal")] -use crate::auth::api::request::ApiTokenRequest; +use crate::{auth::api::request::ApiTokenRequest, client::UserLoginMethod}; use crate::{ auth::api::{request::AccessTokenRequest, response::IdentityTokenResponse}, - client::{Client, LoginMethod}, + client::{Client, LoginMethod, ServiceAccountLoginMethod}, error::{Error, Result}, }; @@ -18,37 +18,41 @@ pub(crate) async fn renew_token(client: &mut Client) -> Result<()> { let res = match login_method { #[cfg(feature = "internal")] - LoginMethod::Username { client_id } => { - let refresh = client - .refresh_token - .as_deref() - .ok_or(Error::NotAuthenticated)?; + LoginMethod::User(u) => match u { + UserLoginMethod::Username { client_id, .. } => { + let refresh = client + .refresh_token + .as_deref() + .ok_or(Error::NotAuthenticated)?; - crate::auth::api::request::RenewTokenRequest::new( - refresh.to_owned(), - client_id.to_owned(), - ) - .send(&client.__api_configurations) - .await? - } - #[cfg(feature = "internal")] - LoginMethod::ApiKey { - client_id, - client_secret, - } => { - ApiTokenRequest::new(client_id, client_secret) - .send(&client.__api_configurations) - .await? - } - LoginMethod::AccessToken { - service_account_id, - client_secret, - .. - } => { - AccessTokenRequest::new(*service_account_id, client_secret) + crate::auth::api::request::RenewTokenRequest::new( + refresh.to_owned(), + client_id.to_owned(), + ) .send(&client.__api_configurations) .await? - } + } + UserLoginMethod::ApiKey { + client_id, + client_secret, + .. + } => { + ApiTokenRequest::new(client_id, client_secret) + .send(&client.__api_configurations) + .await? + } + }, + LoginMethod::ServiceAccount(s) => match s { + ServiceAccountLoginMethod::AccessToken { + service_account_id, + client_secret, + .. + } => { + AccessTokenRequest::new(*service_account_id, client_secret) + .send(&client.__api_configurations) + .await? + } + }, }; match res { diff --git a/crates/bitwarden/src/client/auth_settings.rs b/crates/bitwarden/src/client/auth_settings.rs deleted file mode 100644 index 5e23947a1..000000000 --- a/crates/bitwarden/src/client/auth_settings.rs +++ /dev/null @@ -1,138 +0,0 @@ -use std::num::NonZeroU32; - -use base64::Engine; -#[cfg(feature = "internal")] -use bitwarden_api_identity::models::{KdfType, PreloginResponseModel}; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - -use crate::{ - crypto::{PbkdfSha256Hmac, PBKDF_SHA256_HMAC_OUT_SIZE}, - error::Result, - util::BASE64_ENGINE, -}; - -#[derive(Debug)] -pub(crate) struct AuthSettings { - pub email: String, - pub(crate) kdf: Kdf, -} - -#[derive(Serialize, Deserialize, Debug, JsonSchema)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -#[cfg_attr(feature = "mobile", derive(uniffi::Enum))] -pub enum Kdf { - PBKDF2 { - iterations: NonZeroU32, - }, - Argon2id { - iterations: NonZeroU32, - memory: NonZeroU32, - parallelism: NonZeroU32, - }, -} - -impl AuthSettings { - #[cfg(feature = "internal")] - pub fn new(response: PreloginResponseModel, email: String) -> Self { - use crate::util::{ - default_argon2_iterations, default_argon2_memory, default_argon2_parallelism, - default_pbkdf2_iterations, - }; - - let kdf = match response.kdf.unwrap_or_default() { - KdfType::Variant0 => Kdf::PBKDF2 { - iterations: response - .kdf_iterations - .and_then(|e| NonZeroU32::new(e as u32)) - .unwrap_or_else(default_pbkdf2_iterations), - }, - KdfType::Variant1 => Kdf::Argon2id { - iterations: response - .kdf_iterations - .and_then(|e| NonZeroU32::new(e as u32)) - .unwrap_or_else(default_argon2_iterations), - memory: response - .kdf_memory - .and_then(|e| NonZeroU32::new(e as u32)) - .unwrap_or_else(default_argon2_memory), - parallelism: response - .kdf_parallelism - .and_then(|e| NonZeroU32::new(e as u32)) - .unwrap_or_else(default_argon2_parallelism), - }, - }; - - Self { email, kdf } - } - - pub fn make_user_password_hash(&self, password: &str) -> Result { - self.make_password_hash(password, &self.email) - } - - pub fn make_password_hash(&self, password: &str, salt: &str) -> Result { - let hash: [u8; 32] = - crate::crypto::hash_kdf(password.as_bytes(), salt.as_bytes(), &self.kdf)?; - - // Server expects hash + 1 iteration - let login_hash = pbkdf2::pbkdf2_array::( - &hash, - password.as_bytes(), - 1, - ) - .expect("hash is a valid fixed size"); - - Ok(BASE64_ENGINE.encode(login_hash)) - } -} - -#[cfg(test)] -mod tests { - use bitwarden_api_identity::models::{KdfType, PreloginResponseModel}; - - use super::AuthSettings; - - #[test] - fn test_password_hash_pbkdf2() { - let res = PreloginResponseModel { - kdf: Some(KdfType::Variant0), - kdf_iterations: Some(100_000), - kdf_memory: None, - kdf_parallelism: None, - }; - let settings = AuthSettings::new(res, "test@bitwarden.com".into()); - - assert_eq!( - settings - .make_password_hash("asdfasdf", "test_salt") - .unwrap(), - "ZF6HjxUTSyBHsC+HXSOhZoXN+UuMnygV5YkWXCY4VmM=" - ); - assert_eq!( - settings.make_user_password_hash("asdfasdf").unwrap(), - "wmyadRMyBZOH7P/a/ucTCbSghKgdzDpPqUnu/DAVtSw=" - ); - } - - #[test] - fn test_password_hash_argon2id() { - let res = PreloginResponseModel { - kdf: Some(KdfType::Variant1), - kdf_iterations: Some(4), - kdf_memory: Some(32), - kdf_parallelism: Some(2), - }; - let settings = AuthSettings::new(res, "test@bitwarden.com".into()); - - assert_eq!( - settings - .make_password_hash("asdfasdf", "test_salt") - .unwrap(), - "PR6UjYmjmppTYcdyTiNbAhPJuQQOmynKbdEl1oyi/iQ=" - ); - assert_eq!( - settings.make_user_password_hash("asdfasdf").unwrap(), - "ImYMPyd/X7FPrWzbt+wRfmlICWTA25yZrOob4TBMEZw=" - ); - } -} diff --git a/crates/bitwarden/src/client/client.rs b/crates/bitwarden/src/client/client.rs index 357b13aa1..f6eb0c954 100644 --- a/crates/bitwarden/src/client/client.rs +++ b/crates/bitwarden/src/client/client.rs @@ -1,24 +1,20 @@ use std::time::{Duration, Instant}; -use reqwest::header::{self}; -use uuid::Uuid; #[cfg(feature = "internal")] -use { - crate::{ - auth::login::{ - api_key_login, password_login, send_two_factor_email, ApiKeyLoginRequest, - ApiKeyLoginResponse, PasswordLoginRequest, PasswordLoginResponse, - TwoFactorEmailRequest, - }, - client::auth_settings::AuthSettings, - crypto::EncString, - platform::{ - generate_fingerprint, get_user_api_key, sync, FingerprintRequest, FingerprintResponse, - SecretVerificationRequest, SyncRequest, SyncResponse, UserApiKeyResponse, - }, +use crate::{ + auth::login::{ + api_key_login, password_login, send_two_factor_email, ApiKeyLoginRequest, + ApiKeyLoginResponse, PasswordLoginRequest, PasswordLoginResponse, TwoFactorEmailRequest, + }, + client::kdf::Kdf, + crypto::EncString, + platform::{ + generate_fingerprint, get_user_api_key, sync, FingerprintRequest, FingerprintResponse, + SecretVerificationRequest, SyncRequest, SyncResponse, UserApiKeyResponse, }, - log::debug, }; +use reqwest::header::{self}; +use uuid::Uuid; #[cfg(feature = "secrets")] use crate::auth::login::{access_token_login, AccessTokenLoginRequest, AccessTokenLoginResponse}; @@ -42,12 +38,31 @@ pub(crate) struct ApiConfigurations { #[derive(Debug, Clone)] pub(crate) enum LoginMethod { #[cfg(feature = "internal")] - Username { client_id: String }, - #[cfg(feature = "internal")] + User(UserLoginMethod), + // TODO: Organizations supports api key + // Organization(OrganizationLoginMethod), + ServiceAccount(ServiceAccountLoginMethod), +} + +#[derive(Debug, Clone)] +#[cfg(feature = "internal")] +pub(crate) enum UserLoginMethod { + Username { + client_id: String, + email: String, + kdf: Kdf, + }, ApiKey { client_id: String, client_secret: String, + + email: String, + kdf: Kdf, }, +} + +#[derive(Debug, Clone)] +pub(crate) enum ServiceAccountLoginMethod { AccessToken { service_account_id: Uuid, client_secret: String, @@ -67,9 +82,6 @@ pub struct Client { #[doc(hidden)] pub(crate) __api_configurations: ApiConfigurations, - #[cfg(feature = "internal")] - auth_settings: Option, - encryption_settings: Option, } @@ -114,8 +126,6 @@ impl Client { api, device_type: settings.device_type, }, - #[cfg(feature = "internal")] - auth_settings: None, encryption_settings: None, } } @@ -127,6 +137,13 @@ impl Client { &self.__api_configurations } + #[cfg(feature = "internal")] + pub async fn prelogin(&mut self, email: String) -> Result { + use crate::auth::login::request_prelogin; + + request_prelogin(self, email).await?.try_into() + } + #[cfg(feature = "internal")] pub async fn password_login( &mut self, @@ -165,15 +182,16 @@ impl Client { } #[cfg(feature = "internal")] - pub(crate) fn get_auth_settings(&self) -> &Option { - &self.auth_settings + pub(crate) fn get_login_method(&self) -> &Option { + &self.login_method } pub fn get_access_token_organization(&self) -> Option { - match &self.login_method { - Some(LoginMethod::AccessToken { - organization_id, .. - }) => Some(*organization_id), + match self.login_method { + Some(LoginMethod::ServiceAccount(ServiceAccountLoginMethod::AccessToken { + organization_id, + .. + })) => Some(organization_id), _ => None, } } @@ -182,10 +200,12 @@ impl Client { self.encryption_settings.as_ref().ok_or(Error::VaultLocked) } - #[cfg(feature = "internal")] - pub(crate) fn set_auth_settings(&mut self, auth_settings: AuthSettings) { - debug! {"setting auth settings: {:#?}", auth_settings} - self.auth_settings = Some(auth_settings); + #[cfg(feature = "mobile")] + pub(crate) fn set_login_method(&mut self, login_method: LoginMethod) { + use log::debug; + + debug! {"setting login method: {:#?}", login_method} + self.login_method = Some(login_method); } pub(crate) fn set_tokens( @@ -209,7 +229,7 @@ impl Client { #[cfg(feature = "internal")] pub fn is_authed(&self) -> bool { - self.token.is_some() || self.auth_settings.is_some() + self.token.is_some() || self.login_method.is_some() } #[cfg(feature = "internal")] @@ -219,13 +239,13 @@ impl Client { user_key: EncString, private_key: EncString, ) -> Result<&EncryptionSettings> { - let auth = match &self.auth_settings { - Some(a) => a, - None => return Err(Error::NotAuthenticated), + let login_method = match &self.login_method { + Some(LoginMethod::User(u)) => u, + _ => return Err(Error::NotAuthenticated), }; self.encryption_settings = Some(EncryptionSettings::new( - auth, + login_method, password, user_key, private_key, diff --git a/crates/bitwarden/src/client/client_settings.rs b/crates/bitwarden/src/client/client_settings.rs index a2aed76f1..68ef7b3a9 100644 --- a/crates/bitwarden/src/client/client_settings.rs +++ b/crates/bitwarden/src/client/client_settings.rs @@ -21,7 +21,7 @@ use serde::{Deserialize, Serialize}; /// /// Targets `localhost:8080` for debug builds. #[derive(Serialize, Deserialize, Debug, JsonSchema)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] +#[serde(default, rename_all = "camelCase", deny_unknown_fields)] #[cfg_attr(feature = "mobile", derive(uniffi::Record))] pub struct ClientSettings { /// The identity url of the targeted Bitwarden instance. Defaults to `https://identity.bitwarden.com` @@ -34,19 +34,6 @@ pub struct ClientSettings { pub device_type: DeviceType, } -#[cfg(debug_assertions)] -impl Default for ClientSettings { - fn default() -> Self { - Self { - identity_url: "https://localhost:8080/identity".into(), - api_url: "https://localhost:8080/api".into(), - user_agent: "Bitwarden Rust-SDK".into(), - device_type: DeviceType::SDK, - } - } -} - -#[cfg(not(debug_assertions))] impl Default for ClientSettings { fn default() -> Self { Self { diff --git a/crates/bitwarden/src/client/encryption_settings.rs b/crates/bitwarden/src/client/encryption_settings.rs index 29fd38b42..9c79a1781 100644 --- a/crates/bitwarden/src/client/encryption_settings.rs +++ b/crates/bitwarden/src/client/encryption_settings.rs @@ -4,12 +4,12 @@ use rsa::RsaPrivateKey; use uuid::Uuid; #[cfg(feature = "internal")] use { - crate::client::auth_settings::AuthSettings, + crate::client::UserLoginMethod, rsa::{pkcs8::DecodePrivateKey, Oaep}, }; use crate::{ - crypto::{encrypt_aes256, EncString, SymmetricCryptoKey}, + crypto::{encrypt_aes256_hmac, EncString, SymmetricCryptoKey}, error::{CryptoError, Result}, }; @@ -28,42 +28,38 @@ impl std::fmt::Debug for EncryptionSettings { impl EncryptionSettings { #[cfg(feature = "internal")] pub(crate) fn new( - auth: &AuthSettings, + login_method: &UserLoginMethod, password: &str, user_key: EncString, private_key: EncString, ) -> Result { - use crate::crypto::decrypt_aes256; - - // Stretch keys from the provided password - let (key, mac_key) = crate::crypto::stretch_key_password( - password.as_bytes(), - auth.email.as_bytes(), - &auth.kdf, - )?; - - // Decrypt the user key with the stretched key - let user_key = { - let (iv, mac, data) = match user_key { - EncString::AesCbc256_HmacSha256_B64 { iv, mac, data } => (iv, mac, data), - _ => return Err(CryptoError::InvalidKey.into()), - }; - - let dec = decrypt_aes256(&iv, &mac, data, Some(mac_key), key)?; - SymmetricCryptoKey::try_from(dec.as_slice())? - }; - - // Decrypt the private key with the user key - let private_key = { - let dec = private_key.decrypt_with_key(&user_key)?; - Some(rsa::RsaPrivateKey::from_pkcs8_der(&dec).map_err(|_| CryptoError::InvalidKey)?) - }; - - Ok(EncryptionSettings { - user_key, - private_key, - org_keys: HashMap::new(), - }) + use crate::crypto::MasterKey; + + match login_method { + UserLoginMethod::Username { email, kdf, .. } + | UserLoginMethod::ApiKey { email, kdf, .. } => { + // Derive master key from password + let master_key = MasterKey::derive(password.as_bytes(), email.as_bytes(), kdf)?; + + // Decrypt the user key + let user_key = master_key.decrypt_user_key(user_key)?; + + // Decrypt the private key with the user key + let private_key = { + let dec = private_key.decrypt_with_key(&user_key)?; + Some( + rsa::RsaPrivateKey::from_pkcs8_der(&dec) + .map_err(|_| CryptoError::InvalidKey)?, + ) + }; + + Ok(EncryptionSettings { + user_key, + private_key, + org_keys: HashMap::new(), + }) + } + } } pub(crate) fn new_single_key(key: SymmetricCryptoKey) -> Self { @@ -131,7 +127,7 @@ impl EncryptionSettings { pub(crate) fn encrypt(&self, data: &[u8], org_id: &Option) -> Result { let key = self.get_key(org_id).ok_or(CryptoError::NoKeyForOrg)?; - let dec = encrypt_aes256(data, key.mac_key, key.key)?; + let dec = encrypt_aes256_hmac(data, key.mac_key.ok_or(CryptoError::InvalidMac)?, key.key)?; Ok(dec) } } diff --git a/crates/bitwarden/src/client/kdf.rs b/crates/bitwarden/src/client/kdf.rs new file mode 100644 index 000000000..4f1edfdb0 --- /dev/null +++ b/crates/bitwarden/src/client/kdf.rs @@ -0,0 +1,60 @@ +use std::num::NonZeroU32; + +#[cfg(feature = "internal")] +use bitwarden_api_identity::models::{KdfType, PreloginResponseModel}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +#[cfg(feature = "internal")] +use crate::error::{Error, Result}; + +#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +#[cfg_attr(feature = "mobile", derive(uniffi::Enum))] +pub enum Kdf { + PBKDF2 { + iterations: NonZeroU32, + }, + Argon2id { + iterations: NonZeroU32, + memory: NonZeroU32, + parallelism: NonZeroU32, + }, +} + +#[cfg(feature = "internal")] +impl TryFrom for Kdf { + type Error = Error; + + fn try_from(response: PreloginResponseModel) -> Result { + use crate::util::{ + default_argon2_iterations, default_argon2_memory, default_argon2_parallelism, + default_pbkdf2_iterations, + }; + + let kdf = response.kdf.ok_or(Error::Internal("KDF not found"))?; + + Ok(match kdf { + KdfType::Variant0 => Kdf::PBKDF2 { + iterations: response + .kdf_iterations + .and_then(|e| NonZeroU32::new(e as u32)) + .unwrap_or_else(default_pbkdf2_iterations), + }, + KdfType::Variant1 => Kdf::Argon2id { + iterations: response + .kdf_iterations + .and_then(|e| NonZeroU32::new(e as u32)) + .unwrap_or_else(default_argon2_iterations), + memory: response + .kdf_memory + .and_then(|e| NonZeroU32::new(e as u32)) + .unwrap_or_else(default_argon2_memory), + parallelism: response + .kdf_parallelism + .and_then(|e| NonZeroU32::new(e as u32)) + .unwrap_or_else(default_argon2_parallelism), + }, + }) + } +} diff --git a/crates/bitwarden/src/client/mod.rs b/crates/bitwarden/src/client/mod.rs index c7bd1918c..25a2f5db0 100644 --- a/crates/bitwarden/src/client/mod.rs +++ b/crates/bitwarden/src/client/mod.rs @@ -2,12 +2,11 @@ pub(crate) use client::*; pub(crate) mod access_token; -#[cfg(any(feature = "internal", feature = "mobile"))] -pub mod auth_settings; #[allow(clippy::module_inception)] mod client; pub mod client_settings; pub(crate) mod encryption_settings; +pub mod kdf; pub use access_token::AccessToken; pub use client::Client; diff --git a/crates/bitwarden/src/crypto/aes_ops.rs b/crates/bitwarden/src/crypto/aes_ops.rs index 109deac4f..34d4021e8 100644 --- a/crates/bitwarden/src/crypto/aes_ops.rs +++ b/crates/bitwarden/src/crypto/aes_ops.rs @@ -10,24 +10,7 @@ use crate::{ error::{CryptoError, Result}, }; -pub fn decrypt_aes256( - iv: &[u8; 16], - mac: &[u8; 32], - data: Vec, - mac_key: Option>, - key: GenericArray, -) -> Result> { - let mac_key = match mac_key { - Some(k) => k, - None => return Err(CryptoError::InvalidMac.into()), - }; - - // Validate HMAC - let res = validate_mac(&mac_key, iv, &data)?; - if res != *mac { - return Err(CryptoError::InvalidMac.into()); - } - +pub fn decrypt_aes256(iv: &[u8; 16], data: Vec, key: GenericArray) -> Result> { // Decrypt data let iv = GenericArray::from_slice(iv); let mut data = data; @@ -42,24 +25,44 @@ pub fn decrypt_aes256( Ok(data) } -pub fn encrypt_aes256( +pub fn decrypt_aes256_hmac( + iv: &[u8; 16], + mac: &[u8; 32], + data: Vec, + mac_key: GenericArray, + key: GenericArray, +) -> Result> { + let res = validate_mac(&mac_key, iv, &data)?; + if res != *mac { + return Err(CryptoError::InvalidMac.into()); + } + decrypt_aes256(iv, data, key) +} + +pub fn encrypt_aes256(data_dec: &[u8], key: GenericArray) -> Result { + let (iv, data) = encrypt_aes256_internal(data_dec, key); + + Ok(EncString::AesCbc256_B64 { iv, data }) +} + +pub fn encrypt_aes256_hmac( data_dec: &[u8], - mac_key: Option>, + mac_key: GenericArray, key: GenericArray, ) -> Result { - let mac_key = match mac_key { - Some(k) => k, - None => return Err(CryptoError::InvalidMac.into()), - }; + let (iv, data) = encrypt_aes256_internal(data_dec, key); + let mac = validate_mac(&mac_key, &iv, &data)?; + + Ok(EncString::AesCbc256_HmacSha256_B64 { iv, mac, data }) +} +fn encrypt_aes256_internal(data_dec: &[u8], key: GenericArray) -> ([u8; 16], Vec) { let mut iv = [0u8; 16]; rand::thread_rng().fill_bytes(&mut iv); let data = cbc::Encryptor::::new(&key, &iv.into()) .encrypt_padded_vec_mut::(data_dec); - let mac = validate_mac(&mac_key, &iv, &data)?; - - Ok(EncString::AesCbc256_HmacSha256_B64 { iv, mac, data }) + (iv, data) } fn validate_mac(mac_key: &[u8], iv: &[u8], data: &[u8]) -> Result<[u8; 32]> { diff --git a/crates/bitwarden/src/crypto/enc_string.rs b/crates/bitwarden/src/crypto/enc_string.rs index 2543077ca..b4c691080 100644 --- a/crates/bitwarden/src/crypto/enc_string.rs +++ b/crates/bitwarden/src/crypto/enc_string.rs @@ -6,7 +6,7 @@ use uuid::Uuid; use crate::{ client::encryption_settings::EncryptionSettings, - crypto::{decrypt_aes256, Decryptable, Encryptable, SymmetricCryptoKey}, + crypto::{decrypt_aes256_hmac, Decryptable, Encryptable, SymmetricCryptoKey}, error::{CryptoError, EncStringParseError, Error, Result}, util::BASE64_ENGINE, }; @@ -14,41 +14,30 @@ use crate::{ #[derive(Clone)] #[allow(unused, non_camel_case_types)] pub enum EncString { - // 0 - AesCbc256_B64 { - iv: [u8; 16], - data: Vec, - }, - // 1 + /// 0 + AesCbc256_B64 { iv: [u8; 16], data: Vec }, + /// 1 AesCbc128_HmacSha256_B64 { iv: [u8; 16], mac: [u8; 32], data: Vec, }, - // 2 + /// 2 AesCbc256_HmacSha256_B64 { iv: [u8; 16], mac: [u8; 32], data: Vec, }, - // 3 - Rsa2048_OaepSha256_B64 { - data: Vec, - }, - // 4 - Rsa2048_OaepSha1_B64 { - data: Vec, - }, - // 5 - Rsa2048_OaepSha256_HmacSha256_B64 { - mac: [u8; 32], - data: Vec, - }, - // 6 - Rsa2048_OaepSha1_HmacSha256_B64 { - mac: [u8; 32], - data: Vec, - }, + /// 3 + Rsa2048_OaepSha256_B64 { data: Vec }, + /// 4 + Rsa2048_OaepSha1_B64 { data: Vec }, + /// 5 + #[deprecated] + Rsa2048_OaepSha256_HmacSha256_B64 { data: Vec }, + /// 6 + #[deprecated] + Rsa2048_OaepSha1_HmacSha256_B64 { data: Vec }, } // We manually implement these to make sure we don't print any sensitive data @@ -62,32 +51,43 @@ impl FromStr for EncString { type Err = Error; fn from_str(s: &str) -> Result { - let (enc_type, data) = s.split_once('.').ok_or(EncStringParseError::NoType)?; + let (enc_type, parts): (&str, Vec<_>) = { + let header_parts: Vec<_> = s.split('.').collect(); + + if header_parts.len() == 2 { + (header_parts[0], header_parts[1].split('|').collect()) + } else { + // Support legacy format with no header + let parts: Vec<_> = s.split('|').collect(); + if parts.len() == 3 { + ("1", parts) // AesCbc128_HmacSha256_B64 + } else { + ("0", parts) // AesCbc256_B64 + } + } + }; + + fn from_b64_vec(s: &str) -> Result> { + Ok(BASE64_ENGINE + .decode(s) + .map_err(EncStringParseError::InvalidBase64)?) + } + + fn from_b64(s: &str) -> Result<[u8; N]> { + Ok(from_b64_vec(s)?.try_into().map_err(invalid_len_error(N))?) + } - let parts: Vec<_> = data.split('|').collect(); match (enc_type, parts.len()) { - ("0", 2) => unimplemented!(), + ("0", 2) => { + let iv = from_b64(parts[0])?; + let data = from_b64_vec(parts[1])?; + Ok(EncString::AesCbc256_B64 { iv, data }) + } ("1" | "2", 3) => { - let iv_str = parts[0]; - let data_str = parts[1]; - let mac_str = parts[2]; - - let iv = BASE64_ENGINE - .decode(iv_str) - .map_err(EncStringParseError::InvalidBase64)? - .try_into() - .map_err(invalid_len_error(16))?; - - let mac = BASE64_ENGINE - .decode(mac_str) - .map_err(EncStringParseError::InvalidBase64)? - .try_into() - .map_err(invalid_len_error(32))?; - - let data = BASE64_ENGINE - .decode(data_str) - .map_err(EncStringParseError::InvalidBase64)?; + let iv = from_b64(parts[0])?; + let data = from_b64_vec(parts[1])?; + let mac = from_b64(parts[2])?; if enc_type == "1" { Ok(EncString::AesCbc128_HmacSha256_B64 { iv, mac, data }) @@ -95,19 +95,23 @@ impl FromStr for EncString { Ok(EncString::AesCbc256_HmacSha256_B64 { iv, mac, data }) } } - - ("3" | "4", 1) => { - let data = BASE64_ENGINE - .decode(data) - .map_err(EncStringParseError::InvalidBase64)?; - if enc_type == "3" { - Ok(EncString::Rsa2048_OaepSha256_B64 { data }) - } else { - Ok(EncString::Rsa2048_OaepSha1_B64 { data }) - } + ("3", 1) => { + let data = from_b64_vec(parts[0])?; + Ok(EncString::Rsa2048_OaepSha256_B64 { data }) + } + ("4", 1) => { + let data = from_b64_vec(parts[0])?; + Ok(EncString::Rsa2048_OaepSha1_B64 { data }) + } + #[allow(deprecated)] + ("5", 1) => { + let data = from_b64_vec(parts[0])?; + Ok(EncString::Rsa2048_OaepSha256_HmacSha256_B64 { data }) } - ("5" | "6", 2) => { - unimplemented!() + #[allow(deprecated)] + ("6", 1) => { + let data = from_b64_vec(parts[0])?; + Ok(EncString::Rsa2048_OaepSha1_HmacSha256_B64 { data }) } (enc_type, parts) => Err(EncStringParseError::InvalidType { @@ -127,17 +131,27 @@ impl EncString { } let enc_type = buf[0]; - match enc_type { - 0 => unimplemented!(), - 1 | 2 => { - if buf.len() < 49 { - return Err(EncStringParseError::InvalidLength { - expected: 49, - got: buf.len(), - } - .into()); + fn check_length(buf: &[u8], expected: usize) -> Result<()> { + if buf.len() < expected { + return Err(EncStringParseError::InvalidLength { + expected, + got: buf.len(), } + .into()); + } + Ok(()) + } + + match enc_type { + 0 => { + check_length(buf, 18)?; + let iv = buf[1..17].try_into().unwrap(); + let data = buf[17..].to_vec(); + Ok(EncString::AesCbc256_B64 { iv, data }) + } + 1 | 2 => { + check_length(buf, 50)?; let iv = buf[1..17].try_into().unwrap(); let mac = buf[17..49].try_into().unwrap(); let data = buf[49..].to_vec(); @@ -148,6 +162,28 @@ impl EncString { Ok(EncString::AesCbc256_HmacSha256_B64 { iv, mac, data }) } } + 3 => { + check_length(buf, 2)?; + let data = buf[1..].to_vec(); + Ok(EncString::Rsa2048_OaepSha256_B64 { data }) + } + 4 => { + check_length(buf, 2)?; + let data = buf[1..].to_vec(); + Ok(EncString::Rsa2048_OaepSha1_B64 { data }) + } + #[allow(deprecated)] + 5 => { + check_length(buf, 2)?; + let data = buf[1..].to_vec(); + Ok(EncString::Rsa2048_OaepSha256_HmacSha256_B64 { data }) + } + #[allow(deprecated)] + 6 => { + check_length(buf, 2)?; + let data = buf[1..].to_vec(); + Ok(EncString::Rsa2048_OaepSha1_HmacSha256_B64 { data }) + } _ => Err(EncStringParseError::InvalidType { enc_type: enc_type.to_string(), parts: 1, @@ -175,7 +211,20 @@ impl EncString { buf.extend_from_slice(mac); buf.extend_from_slice(data); } - _ => todo!(), + + EncString::Rsa2048_OaepSha256_B64 { data } + | EncString::Rsa2048_OaepSha1_B64 { data } => { + buf = Vec::with_capacity(1 + data.len()); + buf.push(self.enc_type()); + buf.extend_from_slice(data); + } + #[allow(deprecated)] + EncString::Rsa2048_OaepSha256_HmacSha256_B64 { data } + | EncString::Rsa2048_OaepSha1_HmacSha256_B64 { data } => { + buf = Vec::with_capacity(1 + data.len()); + buf.push(self.enc_type()); + buf.extend_from_slice(data); + } } Ok(buf) @@ -190,8 +239,10 @@ impl Display for EncString { EncString::AesCbc256_HmacSha256_B64 { iv, mac, data } => vec![iv, data, mac], EncString::Rsa2048_OaepSha256_B64 { data } => vec![data], EncString::Rsa2048_OaepSha1_B64 { data } => vec![data], - EncString::Rsa2048_OaepSha256_HmacSha256_B64 { mac, data } => vec![data, mac], - EncString::Rsa2048_OaepSha1_HmacSha256_B64 { mac, data } => vec![data, mac], + #[allow(deprecated)] + EncString::Rsa2048_OaepSha256_HmacSha256_B64 { data } => vec![data], + #[allow(deprecated)] + EncString::Rsa2048_OaepSha1_HmacSha256_B64 { data } => vec![data], }; let encoded_parts: Vec = parts @@ -240,14 +291,16 @@ impl serde::Serialize for EncString { } impl EncString { - fn enc_type(&self) -> u8 { + const fn enc_type(&self) -> u8 { match self { EncString::AesCbc256_B64 { .. } => 0, EncString::AesCbc128_HmacSha256_B64 { .. } => 1, EncString::AesCbc256_HmacSha256_B64 { .. } => 2, EncString::Rsa2048_OaepSha256_B64 { .. } => 3, EncString::Rsa2048_OaepSha1_B64 { .. } => 4, + #[allow(deprecated)] EncString::Rsa2048_OaepSha256_HmacSha256_B64 { .. } => 5, + #[allow(deprecated)] EncString::Rsa2048_OaepSha1_HmacSha256_B64 { .. } => 6, } } @@ -255,7 +308,8 @@ impl EncString { pub fn decrypt_with_key(&self, key: &SymmetricCryptoKey) -> Result> { match self { EncString::AesCbc256_HmacSha256_B64 { iv, mac, data } => { - let dec = decrypt_aes256(iv, mac, data.clone(), key.mac_key, key.key)?; + let mac_key = key.mac_key.ok_or(CryptoError::InvalidMac)?; + let dec = decrypt_aes256_hmac(iv, mac, data.clone(), mac_key, key.key)?; Ok(dec) } _ => Err(CryptoError::InvalidKey.into()), diff --git a/crates/bitwarden/src/crypto/master_key.rs b/crates/bitwarden/src/crypto/master_key.rs index f2df690f9..f908d5299 100644 --- a/crates/bitwarden/src/crypto/master_key.rs +++ b/crates/bitwarden/src/crypto/master_key.rs @@ -1,17 +1,65 @@ -use aes::cipher::typenum::U32; - -use super::hkdf_expand; -use { - crate::{client::auth_settings::Kdf, error::Result}, - aes::cipher::generic_array::GenericArray, - sha2::Digest, +use aes::cipher::{generic_array::GenericArray, typenum::U32}; +use base64::Engine; +use rand::Rng; +use sha2::Digest; + +use super::{ + encrypt_aes256, hkdf_expand, EncString, PbkdfSha256Hmac, SymmetricCryptoKey, UserKey, + PBKDF_SHA256_HMAC_OUT_SIZE, }; +use crate::{client::kdf::Kdf, error::Result, util::BASE64_ENGINE}; + +#[derive(Copy, Clone)] +pub(crate) enum HashPurpose { + ServerAuthorization = 1, + // LocalAuthorization = 2, +} + +/// A Master Key. +pub(crate) struct MasterKey(SymmetricCryptoKey); -pub(crate) fn hash_kdf(secret: &[u8], salt: &[u8], kdf: &Kdf) -> Result<[u8; 32]> { - use crate::crypto::PBKDF_SHA256_HMAC_OUT_SIZE; +impl MasterKey { + /// 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) + } + + /// Derive the master key hash, used for server authorization. + pub(crate) fn derive_master_key_hash( + &self, + password: &[u8], + purpose: HashPurpose, + ) -> Result { + let hash = pbkdf2::pbkdf2_array::( + &self.0.key, + password, + purpose as u32, + ) + .expect("hash is a valid fixed size"); - use super::PbkdfSha256Hmac; + Ok(BASE64_ENGINE.encode(hash)) + } + pub(crate) fn make_user_key(&self) -> Result<(UserKey, EncString)> { + let mut user_key = [0u8; 64]; + rand::thread_rng().fill(&mut user_key); + + let protected = encrypt_aes256(&user_key, self.0.key)?; + + let u: &[u8] = &user_key; + Ok((UserKey::new(SymmetricCryptoKey::try_from(u)?), protected)) + } + + pub(crate) fn decrypt_user_key(&self, user_key: EncString) -> Result { + let stretched_key = stretch_master_key(self)?; + + let dec = user_key.decrypt_with_key(&stretched_key)?; + SymmetricCryptoKey::try_from(dec.as_slice()) + } +} + +/// Derive a generic key from a secret and salt using the provided KDF. +fn derive_key(secret: &[u8], salt: &[u8], kdf: &Kdf) -> Result { let hash = match kdf { Kdf::PBKDF2 { iterations } => pbkdf2::pbkdf2_array::< PbkdfSha256Hmac, @@ -47,32 +95,29 @@ pub(crate) fn hash_kdf(secret: &[u8], salt: &[u8], kdf: &Kdf) -> Result<[u8; 32] hash } }; - Ok(hash) + SymmetricCryptoKey::try_from(hash.as_slice()) } -pub(crate) fn stretch_key_password( - secret: &[u8], - salt: &[u8], - kdf: &Kdf, -) -> Result<(GenericArray, GenericArray)> { - let master_key: [u8; 32] = hash_kdf(secret, salt, kdf)?; - - let key: GenericArray = hkdf_expand(&master_key, Some("enc"))?; - let mac_key: GenericArray = hkdf_expand(&master_key, Some("mac"))?; +fn stretch_master_key(master_key: &MasterKey) -> Result { + let key: GenericArray = hkdf_expand(&master_key.0.key, Some("enc"))?; + let mac_key: GenericArray = hkdf_expand(&master_key.0.key, Some("mac"))?; - Ok((key, mac_key)) + Ok(SymmetricCryptoKey { + key, + mac_key: Some(mac_key), + }) } #[cfg(test)] mod tests { - use { - crate::{client::auth_settings::Kdf, crypto::stretch_key_password}, - std::num::NonZeroU32, - }; + use std::num::NonZeroU32; + + use super::{stretch_master_key, HashPurpose, MasterKey}; + use crate::{client::kdf::Kdf, crypto::SymmetricCryptoKey}; #[test] - fn test_key_stretch_password_pbkdf2() { - let (key, mac) = stretch_key_password( + fn test_master_key_derive_pbkdf2() { + let master_key = MasterKey::derive( &b"67t9b5g67$%Dh89n"[..], "test_key".as_bytes(), &Kdf::PBKDF2 { @@ -82,24 +127,18 @@ mod tests { .unwrap(); assert_eq!( - key.as_slice(), - [ - 111, 31, 178, 45, 238, 152, 37, 114, 143, 215, 124, 83, 135, 173, 195, 23, 142, - 134, 120, 249, 61, 132, 163, 182, 113, 197, 189, 204, 188, 21, 237, 96 - ] - ); - assert_eq!( - mac.as_slice(), [ - 221, 127, 206, 234, 101, 27, 202, 38, 86, 52, 34, 28, 78, 28, 185, 16, 48, 61, 127, - 166, 209, 247, 194, 87, 232, 26, 48, 85, 193, 249, 179, 155 - ] + 31, 79, 104, 226, 150, 71, 177, 90, 194, 80, 172, 209, 17, 129, 132, 81, 138, 167, + 69, 167, 254, 149, 2, 27, 39, 197, 64, 42, 22, 195, 86, 75 + ], + master_key.0.key.as_slice() ); + assert_eq!(None, master_key.0.mac_key); } #[test] - fn test_key_stretch_password_argon2() { - let (key, mac) = stretch_key_password( + fn test_master_key_derive_argon2() { + let master_key = MasterKey::derive( &b"67t9b5g67$%Dh89n"[..], "test_key".as_bytes(), &Kdf::Argon2id { @@ -111,18 +150,79 @@ mod tests { .unwrap(); assert_eq!( - key.as_slice(), [ - 236, 253, 166, 121, 207, 124, 98, 149, 42, 141, 97, 226, 207, 71, 173, 60, 10, 0, - 184, 255, 252, 87, 62, 32, 188, 166, 173, 223, 146, 159, 222, 219 + 207, 240, 225, 177, 162, 19, 163, 76, 98, 106, 179, 175, 224, 9, 17, 240, 20, 147, + 237, 47, 246, 150, 141, 184, 62, 225, 131, 242, 51, 53, 225, 242 + ], + master_key.0.key.as_slice() + ); + assert_eq!(None, master_key.0.mac_key); + } + + #[test] + fn test_stretch_master_key() { + let master_key = MasterKey(SymmetricCryptoKey { + key: [ + 31, 79, 104, 226, 150, 71, 177, 90, 194, 80, 172, 209, 17, 129, 132, 81, 138, 167, + 69, 167, 254, 149, 2, 27, 39, 197, 64, 42, 22, 195, 86, 75, ] + .into(), + mac_key: None, + }); + + let stretched = stretch_master_key(&master_key).unwrap(); + + assert_eq!( + [ + 111, 31, 178, 45, 238, 152, 37, 114, 143, 215, 124, 83, 135, 173, 195, 23, 142, + 134, 120, 249, 61, 132, 163, 182, 113, 197, 189, 204, 188, 21, 237, 96 + ], + stretched.key.as_slice() ); assert_eq!( - mac.as_slice(), [ - 214, 144, 76, 173, 225, 106, 132, 131, 173, 56, 134, 241, 223, 227, 165, 161, 146, - 37, 111, 206, 155, 24, 224, 151, 134, 189, 202, 0, 27, 149, 131, 21 - ] + 221, 127, 206, 234, 101, 27, 202, 38, 86, 52, 34, 28, 78, 28, 185, 16, 48, 61, 127, + 166, 209, 247, 194, 87, 232, 26, 48, 85, 193, 249, 179, 155 + ], + stretched.mac_key.unwrap().as_slice() + ); + } + + #[test] + fn test_password_hash_pbkdf2() { + let password = "asdfasdf".as_bytes(); + let salt = "test_salt".as_bytes(); + let kdf = Kdf::PBKDF2 { + iterations: NonZeroU32::new(100_000).unwrap(), + }; + + let master_key = MasterKey::derive(password, salt, &kdf).unwrap(); + + assert_eq!( + "ZF6HjxUTSyBHsC+HXSOhZoXN+UuMnygV5YkWXCY4VmM=", + master_key + .derive_master_key_hash(password, HashPurpose::ServerAuthorization) + .unwrap(), + ); + } + + #[test] + fn test_password_hash_argon2id() { + let password = "asdfasdf".as_bytes(); + let salt = "test_salt".as_bytes(); + let kdf = Kdf::Argon2id { + iterations: NonZeroU32::new(4).unwrap(), + memory: NonZeroU32::new(32).unwrap(), + parallelism: NonZeroU32::new(2).unwrap(), + }; + + let master_key = MasterKey::derive(password, salt, &kdf).unwrap(); + + assert_eq!( + "PR6UjYmjmppTYcdyTiNbAhPJuQQOmynKbdEl1oyi/iQ=", + master_key + .derive_master_key_hash(password, HashPurpose::ServerAuthorization) + .unwrap(), ); } } diff --git a/crates/bitwarden/src/crypto/mod.rs b/crates/bitwarden/src/crypto/mod.rs index 7c39c754e..4c7cefa7c 100644 --- a/crates/bitwarden/src/crypto/mod.rs +++ b/crates/bitwarden/src/crypto/mod.rs @@ -2,11 +2,12 @@ //! //! This module contains the cryptographic primitives used throughout the SDK. The module makes a //! best effort to abstract away cryptographic concepts into concepts such as -//! [`EncString`][EncString] and [`SymmetricCryptoKey`][SymmetricCryptoKey]. +//! [`EncString`] and [`SymmetricCryptoKey`]. //! //! ## Conventions: //! //! - Pure Functions that deterministically "derive" keys from input are prefixed with `derive_`. +//! - Functions that generate new keys are prefixed with `make_`. //! //! ## Differences from [`clients`](https://github.com/bitwarden/clients) //! @@ -16,6 +17,8 @@ //! //! - `CryptoService.makeSendKey` & `AccessService.createAccessToken` are replaced by the generic //! `derive_shareable_key` +//! - MasterKey operations such as `makeMasterKey` and `hashMasterKey` are moved to the MasterKey +//! struct. //! use aes::cipher::{generic_array::GenericArray, ArrayLength, Unsigned}; @@ -28,7 +31,7 @@ pub use enc_string::EncString; mod encryptable; pub use encryptable::{Decryptable, Encryptable}; mod aes_ops; -pub use aes_ops::{decrypt_aes256, encrypt_aes256}; +pub use aes_ops::{decrypt_aes256, decrypt_aes256_hmac, encrypt_aes256, encrypt_aes256_hmac}; mod symmetric_crypto_key; pub use symmetric_crypto_key::SymmetricCryptoKey; mod shareable_key; @@ -37,7 +40,15 @@ pub(crate) use shareable_key::derive_shareable_key; #[cfg(feature = "internal")] mod master_key; #[cfg(feature = "internal")] -pub(crate) use master_key::{hash_kdf, stretch_key_password}; +pub(crate) use master_key::{HashPurpose, MasterKey}; +#[cfg(feature = "internal")] +mod user_key; +#[cfg(feature = "internal")] +pub(crate) use user_key::UserKey; +#[cfg(feature = "internal")] +mod rsa; +#[cfg(feature = "internal")] +pub use self::rsa::RsaKeyPair; #[cfg(feature = "mobile")] mod chunked_decryptor; diff --git a/crates/bitwarden/src/crypto/rsa.rs b/crates/bitwarden/src/crypto/rsa.rs new file mode 100644 index 000000000..0d2d135b9 --- /dev/null +++ b/crates/bitwarden/src/crypto/rsa.rs @@ -0,0 +1,42 @@ +use base64::Engine; +use rsa::{ + pkcs8::{EncodePrivateKey, EncodePublicKey}, + RsaPrivateKey, RsaPublicKey, +}; + +use crate::{ + crypto::{encrypt_aes256_hmac, EncString, SymmetricCryptoKey}, + error::{Error, Result}, + util::BASE64_ENGINE, +}; + +#[cfg_attr(feature = "mobile", derive(uniffi::Record))] +pub struct RsaKeyPair { + /// Base64 encoded DER representation of the public key + pub public: String, + /// Encrypted PKCS8 private key + pub private: EncString, +} + +pub(super) fn make_key_pair(key: &SymmetricCryptoKey) -> Result { + let mut rng = rand::thread_rng(); + let bits = 2048; + let priv_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key"); + let pub_key = RsaPublicKey::from(&priv_key); + + let spki = pub_key + .to_public_key_der() + .map_err(|_| Error::Internal("unable to create public key"))?; + + let b64 = BASE64_ENGINE.encode(spki.as_bytes()); + let pkcs = priv_key + .to_pkcs8_der() + .map_err(|_| Error::Internal("unable to create private key"))?; + + let protected = encrypt_aes256_hmac(pkcs.as_bytes(), key.mac_key.unwrap(), key.key)?; + + Ok(RsaKeyPair { + public: b64, + private: protected, + }) +} diff --git a/crates/bitwarden/src/crypto/symmetric_crypto_key.rs b/crates/bitwarden/src/crypto/symmetric_crypto_key.rs index 4d0f573d9..7b2086f75 100644 --- a/crates/bitwarden/src/crypto/symmetric_crypto_key.rs +++ b/crates/bitwarden/src/crypto/symmetric_crypto_key.rs @@ -9,6 +9,7 @@ use crate::{ util::BASE64_ENGINE, }; +/// A symmetric encryption key. Used to encrypt and decrypt [`EncString`](crate::crypto::EncString) pub struct SymmetricCryptoKey { pub key: GenericArray, pub mac_key: Option>, diff --git a/crates/bitwarden/src/crypto/user_key.rs b/crates/bitwarden/src/crypto/user_key.rs new file mode 100644 index 000000000..0fe560665 --- /dev/null +++ b/crates/bitwarden/src/crypto/user_key.rs @@ -0,0 +1,19 @@ +use crate::{ + crypto::{ + rsa::{make_key_pair, RsaKeyPair}, + SymmetricCryptoKey, + }, + error::Result, +}; + +pub(crate) struct UserKey(SymmetricCryptoKey); + +impl UserKey { + pub(crate) fn new(key: SymmetricCryptoKey) -> Self { + Self(key) + } + + pub(crate) fn make_key_pair(&self) -> Result { + make_key_pair(&self.0) + } +} diff --git a/crates/bitwarden/src/error.rs b/crates/bitwarden/src/error.rs index fb4d2fd02..81bbef8e2 100644 --- a/crates/bitwarden/src/error.rs +++ b/crates/bitwarden/src/error.rs @@ -40,6 +40,8 @@ pub enum Error { Io(#[from] std::io::Error), #[error(transparent)] InvalidBase64(#[from] base64::DecodeError), + #[error(transparent)] + Chrono(#[from] chrono::ParseError), #[error("Received error message from server: [{}] {}", .status, .message)] ResponseContent { status: StatusCode, message: String }, diff --git a/crates/bitwarden/src/lib.rs b/crates/bitwarden/src/lib.rs index dd3f40768..31af93419 100644 --- a/crates/bitwarden/src/lib.rs +++ b/crates/bitwarden/src/lib.rs @@ -61,7 +61,7 @@ pub mod mobile; pub mod platform; #[cfg(feature = "secrets")] pub mod secrets_manager; -#[cfg(feature = "internal")] +#[cfg(feature = "mobile")] pub mod tool; #[cfg(feature = "mobile")] pub(crate) mod uniffi_support; diff --git a/crates/bitwarden/src/mobile/client_kdf.rs b/crates/bitwarden/src/mobile/client_kdf.rs index 92d226b59..320a03571 100644 --- a/crates/bitwarden/src/mobile/client_kdf.rs +++ b/crates/bitwarden/src/mobile/client_kdf.rs @@ -1,4 +1,4 @@ -use crate::{client::auth_settings::Kdf, error::Result, mobile::kdf::hash_password, Client}; +use crate::{client::kdf::Kdf, error::Result, mobile::kdf::hash_password, Client}; pub struct ClientKdf<'a> { pub(crate) client: &'a crate::Client, diff --git a/crates/bitwarden/src/mobile/crypto.rs b/crates/bitwarden/src/mobile/crypto.rs index fec569cfe..f4d26dc6c 100644 --- a/crates/bitwarden/src/mobile/crypto.rs +++ b/crates/bitwarden/src/mobile/crypto.rs @@ -3,12 +3,7 @@ use std::collections::HashMap; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use crate::{ - client::auth_settings::{AuthSettings, Kdf}, - crypto::EncString, - error::Result, - Client, -}; +use crate::{client::kdf::Kdf, crypto::EncString, error::Result, Client}; #[cfg(feature = "internal")] #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -31,11 +26,12 @@ pub struct InitCryptoRequest { #[cfg(feature = "internal")] pub async fn initialize_crypto(client: &mut Client, req: InitCryptoRequest) -> Result<()> { - let auth_settings = AuthSettings { + let login_method = crate::client::LoginMethod::User(crate::client::UserLoginMethod::Username { + client_id: "".to_string(), email: req.email, kdf: req.kdf_params, - }; - client.set_auth_settings(auth_settings); + }); + client.set_login_method(login_method); let user_key = req.user_key.parse::()?; let private_key = req.private_key.parse::()?; diff --git a/crates/bitwarden/src/mobile/kdf.rs b/crates/bitwarden/src/mobile/kdf.rs index bebf73423..a34b339bb 100644 --- a/crates/bitwarden/src/mobile/kdf.rs +++ b/crates/bitwarden/src/mobile/kdf.rs @@ -1,5 +1,6 @@ use crate::{ - client::auth_settings::{AuthSettings, Kdf}, + client::kdf::Kdf, + crypto::{HashPurpose, MasterKey}, error::Result, Client, }; @@ -10,10 +11,7 @@ pub async fn hash_password( password: String, kdf_params: Kdf, ) -> Result { - let auth_settings = AuthSettings { - email, - kdf: kdf_params, - }; - let hash = auth_settings.make_user_password_hash(&password)?; - Ok(hash) + let master_key = MasterKey::derive(password.as_bytes(), email.as_bytes(), &kdf_params)?; + + master_key.derive_master_key_hash(password.as_bytes(), HashPurpose::ServerAuthorization) } diff --git a/crates/bitwarden/src/mobile/vault/client_sends.rs b/crates/bitwarden/src/mobile/vault/client_sends.rs index 64cee6407..c131afdad 100644 --- a/crates/bitwarden/src/mobile/vault/client_sends.rs +++ b/crates/bitwarden/src/mobile/vault/client_sends.rs @@ -1,5 +1,6 @@ use std::path::Path; +use super::client_vault::ClientVault; use crate::{ crypto::{Decryptable, EncString, Encryptable}, error::Result, @@ -7,8 +8,6 @@ use crate::{ Client, }; -use super::client_vault::ClientVault; - pub struct ClientSends<'a> { pub(crate) client: &'a Client, } diff --git a/crates/bitwarden/src/platform/get_user_api_key.rs b/crates/bitwarden/src/platform/get_user_api_key.rs index 98035ad45..a1cfc389d 100644 --- a/crates/bitwarden/src/platform/get_user_api_key.rs +++ b/crates/bitwarden/src/platform/get_user_api_key.rs @@ -8,7 +8,8 @@ use serde::{Deserialize, Serialize}; use super::SecretVerificationRequest; use crate::{ - client::auth_settings::AuthSettings, + client::{LoginMethod, UserLoginMethod}, + crypto::{HashPurpose, MasterKey}, error::{Error, Result}, Client, }; @@ -20,7 +21,7 @@ pub(crate) async fn get_user_api_key( info!("Getting Api Key"); debug!("{:?}", input); - let auth_settings = get_auth_settings(client)?; + let auth_settings = get_login_method(client)?; let request = get_secret_verification_request(auth_settings, input)?; let config = client.get_api_configurations().await; @@ -29,33 +30,40 @@ pub(crate) async fn get_user_api_key( UserApiKeyResponse::process_response(response) } -fn get_auth_settings(client: &Client) -> Result<&AuthSettings> { +fn get_login_method(client: &Client) -> Result<&LoginMethod> { if client.is_authed() { - let auth_settings = client - .get_auth_settings() + client + .get_login_method() .as_ref() - .ok_or(Error::NotAuthenticated)?; - Ok(auth_settings) + .ok_or(Error::NotAuthenticated) } else { Err(Error::NotAuthenticated) } } fn get_secret_verification_request( - auth_settings: &AuthSettings, + login_method: &LoginMethod, input: &SecretVerificationRequest, ) -> Result { - let master_password_hash = input - .master_password - .as_ref() - .map(|p| auth_settings.make_user_password_hash(p)) - .transpose()?; - Ok(SecretVerificationRequestModel { - master_password_hash, - otp: input.otp.as_ref().cloned(), - secret: None, - auth_request_access_code: None, - }) + if let LoginMethod::User(UserLoginMethod::Username { email, kdf, .. }) = login_method { + let master_password_hash = input + .master_password + .as_ref() + .map(|p| { + let master_key = MasterKey::derive(p.as_bytes(), email.as_bytes(), kdf)?; + + master_key.derive_master_key_hash(p.as_bytes(), HashPurpose::ServerAuthorization) + }) + .transpose()?; + Ok(SecretVerificationRequestModel { + master_password_hash, + otp: input.otp.as_ref().cloned(), + secret: None, + auth_request_access_code: None, + }) + } else { + Err(Error::Internal("Unsupported login method")) + } } #[derive(Serialize, Deserialize, Debug, JsonSchema)] diff --git a/crates/bitwarden/src/secrets_manager/projects/project_response.rs b/crates/bitwarden/src/secrets_manager/projects/project_response.rs index 52980c45f..b8c82806b 100644 --- a/crates/bitwarden/src/secrets_manager/projects/project_response.rs +++ b/crates/bitwarden/src/secrets_manager/projects/project_response.rs @@ -1,4 +1,5 @@ use bitwarden_api_api::models::ProjectResponseModel; +use chrono::{DateTime, Utc}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -12,12 +13,11 @@ use crate::{ #[derive(Serialize, Deserialize, Debug, JsonSchema)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct ProjectResponse { - pub object: String, pub id: Uuid, pub organization_id: Uuid, pub name: String, - pub creation_date: String, - pub revision_date: String, + pub creation_date: DateTime, + pub revision_date: DateTime, } impl ProjectResponse { @@ -34,14 +34,18 @@ impl ProjectResponse { .decrypt(enc, &Some(organization_id))?; Ok(ProjectResponse { - object: "project".to_owned(), - id: response.id.ok_or(Error::MissingFields)?, organization_id, name, - creation_date: response.creation_date.ok_or(Error::MissingFields)?, - revision_date: response.revision_date.ok_or(Error::MissingFields)?, + creation_date: response + .creation_date + .ok_or(Error::MissingFields)? + .parse()?, + revision_date: response + .revision_date + .ok_or(Error::MissingFields)? + .parse()?, }) } } diff --git a/crates/bitwarden/src/secrets_manager/secrets/secret_response.rs b/crates/bitwarden/src/secrets_manager/secrets/secret_response.rs index 635d1e3c0..a7fe49200 100644 --- a/crates/bitwarden/src/secrets_manager/secrets/secret_response.rs +++ b/crates/bitwarden/src/secrets_manager/secrets/secret_response.rs @@ -1,6 +1,7 @@ use bitwarden_api_api::models::{ BaseSecretResponseModel, BaseSecretResponseModelListResponseModel, SecretResponseModel, }; +use chrono::{DateTime, Utc}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -14,7 +15,6 @@ use crate::{ #[derive(Serialize, Deserialize, Debug, JsonSchema)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct SecretResponse { - pub object: String, pub id: Uuid, pub organization_id: Uuid, pub project_id: Option, @@ -23,8 +23,8 @@ pub struct SecretResponse { pub value: String, pub note: String, - pub creation_date: String, - pub revision_date: String, + pub creation_date: DateTime, + pub revision_date: DateTime, } impl SecretResponse { @@ -73,7 +73,6 @@ impl SecretResponse { .and_then(|p| p.id); Ok(SecretResponse { - object: "secret".to_owned(), id: response.id.ok_or(Error::MissingFields)?, organization_id: org_id.ok_or(Error::MissingFields)?, project_id: project, @@ -81,8 +80,14 @@ impl SecretResponse { value, note, - creation_date: response.creation_date.ok_or(Error::MissingFields)?, - revision_date: response.revision_date.ok_or(Error::MissingFields)?, + creation_date: response + .creation_date + .ok_or(Error::MissingFields)? + .parse()?, + revision_date: response + .revision_date + .ok_or(Error::MissingFields)? + .parse()?, }) } } diff --git a/crates/bitwarden/src/tool/client_generator.rs b/crates/bitwarden/src/tool/client_generator.rs deleted file mode 100644 index 55d1acbba..000000000 --- a/crates/bitwarden/src/tool/client_generator.rs +++ /dev/null @@ -1,25 +0,0 @@ -use crate::{ - error::Result, - tool::password::{PassphraseGeneratorRequest, PasswordGeneratorRequest}, - Client, -}; - -pub struct ClientGenerator<'a> { - pub(crate) _client: &'a crate::Client, -} - -impl<'a> ClientGenerator<'a> { - pub async fn password(&self, _input: PasswordGeneratorRequest) -> Result { - todo!() - } - - pub async fn passphrase(&self, _input: PassphraseGeneratorRequest) -> Result { - todo!() - } -} - -impl<'a> Client { - pub fn generator(&'a self) -> ClientGenerator<'a> { - ClientGenerator { _client: self } - } -} diff --git a/crates/bitwarden/src/tool/exporters/client_exporter.rs b/crates/bitwarden/src/tool/exporters/client_exporter.rs new file mode 100644 index 000000000..f14cbc846 --- /dev/null +++ b/crates/bitwarden/src/tool/exporters/client_exporter.rs @@ -0,0 +1,36 @@ +use crate::{ + error::Result, + tool::exporters::{export_organization_vault, export_vault, ExportFormat}, + vault::{Cipher, Collection, Folder}, + Client, +}; + +pub struct ClientExporters<'a> { + pub(crate) _client: &'a crate::Client, +} + +impl<'a> ClientExporters<'a> { + pub async fn export_vault( + &self, + folders: Vec, + ciphers: Vec, + format: ExportFormat, + ) -> Result { + export_vault(folders, ciphers, format) + } + + pub async fn export_organization_vault( + &self, + collections: Vec, + ciphers: Vec, + format: ExportFormat, + ) -> Result { + export_organization_vault(collections, ciphers, format) + } +} + +impl<'a> Client { + pub fn exporters(&'a self) -> ClientExporters<'a> { + ClientExporters { _client: self } + } +} diff --git a/crates/bitwarden/src/tool/exporters/mod.rs b/crates/bitwarden/src/tool/exporters/mod.rs new file mode 100644 index 000000000..508aae8fb --- /dev/null +++ b/crates/bitwarden/src/tool/exporters/mod.rs @@ -0,0 +1,33 @@ +use schemars::JsonSchema; + +use crate::{ + error::Result, + vault::{Cipher, Collection, Folder}, +}; + +mod client_exporter; + +#[derive(JsonSchema)] +#[cfg_attr(feature = "mobile", derive(uniffi::Enum))] +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, + _format: ExportFormat, +) -> Result { + todo!(); +} + +pub(super) fn export_organization_vault( + _collections: Vec, + _ciphers: Vec, + _format: ExportFormat, +) -> Result { + todo!(); +} diff --git a/crates/bitwarden/src/tool/generators/client_generator.rs b/crates/bitwarden/src/tool/generators/client_generator.rs new file mode 100644 index 000000000..0384eec6a --- /dev/null +++ b/crates/bitwarden/src/tool/generators/client_generator.rs @@ -0,0 +1,27 @@ +use crate::{ + error::Result, + tool::generators::password::{ + passphrase, password, PassphraseGeneratorRequest, PasswordGeneratorRequest, + }, + Client, +}; + +pub struct ClientGenerator<'a> { + pub(crate) _client: &'a crate::Client, +} + +impl<'a> ClientGenerator<'a> { + pub async fn password(&self, input: PasswordGeneratorRequest) -> Result { + password(input) + } + + pub async fn passphrase(&self, input: PassphraseGeneratorRequest) -> Result { + passphrase(input) + } +} + +impl<'a> Client { + pub fn generator(&'a self) -> ClientGenerator<'a> { + ClientGenerator { _client: self } + } +} diff --git a/crates/bitwarden/src/tool/generators/mod.rs b/crates/bitwarden/src/tool/generators/mod.rs new file mode 100644 index 000000000..bdc0fb260 --- /dev/null +++ b/crates/bitwarden/src/tool/generators/mod.rs @@ -0,0 +1,4 @@ +mod client_generator; +mod password; + +pub use password::{PassphraseGeneratorRequest, PasswordGeneratorRequest}; diff --git a/crates/bitwarden/src/tool/password.rs b/crates/bitwarden/src/tool/generators/password.rs similarity index 82% rename from crates/bitwarden/src/tool/password.rs rename to crates/bitwarden/src/tool/generators/password.rs index 144ff0d1c..0a3874082 100644 --- a/crates/bitwarden/src/tool/password.rs +++ b/crates/bitwarden/src/tool/generators/password.rs @@ -1,3 +1,4 @@ +use crate::error::Result; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -38,3 +39,11 @@ pub struct PassphraseGeneratorRequest { pub capitalize: Option, pub include_number: Option, } + +pub(super) fn password(_input: PasswordGeneratorRequest) -> Result { + Ok("pa11w0rd".to_string()) +} + +pub(super) fn passphrase(_input: PassphraseGeneratorRequest) -> Result { + Ok("correct-horse-battery-staple".to_string()) +} diff --git a/crates/bitwarden/src/tool/mod.rs b/crates/bitwarden/src/tool/mod.rs index bdc0fb260..2130a6b0c 100644 --- a/crates/bitwarden/src/tool/mod.rs +++ b/crates/bitwarden/src/tool/mod.rs @@ -1,4 +1,5 @@ -mod client_generator; -mod password; +mod exporters; +mod generators; -pub use password::{PassphraseGeneratorRequest, PasswordGeneratorRequest}; +pub use exporters::ExportFormat; +pub use generators::{PassphraseGeneratorRequest, PasswordGeneratorRequest}; diff --git a/crates/bitwarden/src/vault/cipher/cipher.rs b/crates/bitwarden/src/vault/cipher/cipher.rs index c32f39c30..9bf4bbd25 100644 --- a/crates/bitwarden/src/vault/cipher/cipher.rs +++ b/crates/bitwarden/src/vault/cipher/cipher.rs @@ -44,7 +44,7 @@ pub struct Cipher { pub collection_ids: Vec, pub name: EncString, - pub notes: EncString, + pub notes: Option, pub r#type: CipherType, pub login: Option, @@ -59,9 +59,9 @@ pub struct Cipher { pub view_password: bool, pub local_data: Option, - pub attachments: Vec, - pub fields: Vec, - pub password_history: Vec, + pub attachments: Option>, + pub fields: Option>, + pub password_history: Option>, pub creation_date: DateTime, pub deleted_date: Option>, @@ -78,7 +78,7 @@ pub struct CipherView { pub collection_ids: Vec, pub name: String, - pub notes: String, + pub notes: Option, pub r#type: CipherType, pub login: Option, @@ -93,9 +93,9 @@ pub struct CipherView { pub view_password: bool, pub local_data: Option, - pub attachments: Vec, - pub fields: Vec, - pub password_history: Vec, + pub attachments: Option>, + pub fields: Option>, + pub password_history: Option>, pub creation_date: DateTime, pub deleted_date: Option>, @@ -202,7 +202,7 @@ impl Cipher { let Some(login) = &self.login else { return Ok(String::new()); }; - login.username.decrypt(enc, org_id).unwrap_or_default() + login.username.decrypt(enc, org_id)?.unwrap_or_default() } CipherType::SecureNote => String::new(), CipherType::Card => { @@ -273,7 +273,11 @@ impl Decryptable for Cipher { reprompt: self.reprompt, edit: self.edit, view_password: self.view_password, - attachments: self.attachments.len() as u32, + attachments: self + .attachments + .as_ref() + .map(|a| a.len() as u32) + .unwrap_or(0), creation_date: self.creation_date, deleted_date: self.deleted_date, revision_date: self.revision_date, diff --git a/crates/bitwarden/src/vault/cipher/field.rs b/crates/bitwarden/src/vault/cipher/field.rs index df492eac6..9fa05257a 100644 --- a/crates/bitwarden/src/vault/cipher/field.rs +++ b/crates/bitwarden/src/vault/cipher/field.rs @@ -24,8 +24,8 @@ pub enum FieldType { #[serde(rename_all = "camelCase", deny_unknown_fields)] #[cfg_attr(feature = "mobile", derive(uniffi::Record))] pub struct Field { - name: EncString, - value: EncString, + name: Option, + value: Option, r#type: FieldType, linked_id: Option, @@ -35,8 +35,8 @@ pub struct Field { #[serde(rename_all = "camelCase", deny_unknown_fields)] #[cfg_attr(feature = "mobile", derive(uniffi::Record))] pub struct FieldView { - name: String, - value: String, + name: Option, + value: Option, r#type: FieldType, linked_id: Option, diff --git a/crates/bitwarden/src/vault/cipher/login.rs b/crates/bitwarden/src/vault/cipher/login.rs index bbcb7b232..941aed221 100644 --- a/crates/bitwarden/src/vault/cipher/login.rs +++ b/crates/bitwarden/src/vault/cipher/login.rs @@ -27,7 +27,7 @@ pub enum UriMatchType { #[serde(rename_all = "camelCase", deny_unknown_fields)] #[cfg_attr(feature = "mobile", derive(uniffi::Record))] pub struct LoginUri { - pub uri: EncString, + pub uri: Option, pub r#match: Option, } @@ -35,7 +35,7 @@ pub struct LoginUri { #[serde(rename_all = "camelCase", deny_unknown_fields)] #[cfg_attr(feature = "mobile", derive(uniffi::Record))] pub struct LoginUriView { - pub uri: String, + pub uri: Option, pub r#match: Option, } @@ -43,11 +43,11 @@ pub struct LoginUriView { #[serde(rename_all = "camelCase", deny_unknown_fields)] #[cfg_attr(feature = "mobile", derive(uniffi::Record))] pub struct Login { - pub username: EncString, - pub password: EncString, + pub username: Option, + pub password: Option, pub password_revision_date: Option>, - pub uris: Vec, + pub uris: Option>, pub totp: Option, pub autofill_on_page_load: Option, } @@ -56,11 +56,11 @@ pub struct Login { #[serde(rename_all = "camelCase", deny_unknown_fields)] #[cfg_attr(feature = "mobile", derive(uniffi::Record))] pub struct LoginView { - pub username: String, - pub password: String, + pub username: Option, + pub password: Option, pub password_revision_date: Option>, - pub uris: Vec, + pub uris: Option>, pub totp: Option, pub autofill_on_page_load: Option, } diff --git a/crates/bitwarden/src/vault/send.rs b/crates/bitwarden/src/vault/send.rs index c44e3e55c..935d32eb9 100644 --- a/crates/bitwarden/src/vault/send.rs +++ b/crates/bitwarden/src/vault/send.rs @@ -40,7 +40,7 @@ pub struct SendFileView { #[serde(rename_all = "camelCase", deny_unknown_fields)] #[cfg_attr(feature = "mobile", derive(uniffi::Record))] pub struct SendText { - pub text: EncString, + pub text: Option, pub hidden: bool, } @@ -48,7 +48,7 @@ pub struct SendText { #[serde(rename_all = "camelCase", deny_unknown_fields)] #[cfg_attr(feature = "mobile", derive(uniffi::Record))] pub struct SendTextView { - pub text: String, + pub text: Option, pub hidden: bool, } @@ -348,16 +348,14 @@ pub async fn download_send_file_from_url( #[cfg(test)] mod tests { use super::Send; - use crate::client::{ - auth_settings::{AuthSettings, Kdf}, - encryption_settings::EncryptionSettings, - }; + use crate::client::{encryption_settings::EncryptionSettings, kdf::Kdf, UserLoginMethod}; #[test] fn test_get_send_key() { // Initialize user encryption with some test data let enc = EncryptionSettings::new( - &AuthSettings { + &UserLoginMethod::Username { + client_id: "test".into(), email: "test@bitwarden.com".into(), kdf: Kdf::PBKDF2 { iterations: 345123.try_into().unwrap(), diff --git a/crates/bw/Cargo.toml b/crates/bw/Cargo.toml index bb1621184..29252d133 100644 --- a/crates/bw/Cargo.toml +++ b/crates/bw/Cargo.toml @@ -24,6 +24,7 @@ inquire = "0.6.2" bitwarden = { path = "../bitwarden", version = "0.3.0", features = [ "internal", + "mobile", ] } bitwarden-cli = { path = "../bitwarden-cli", version = "0.1.0" } diff --git a/crates/bw/README.md b/crates/bw/README.md index 2237c0a47..ef3d1fe8b 100644 --- a/crates/bw/README.md +++ b/crates/bw/README.md @@ -1,3 +1,5 @@ # Reserved Bitwarden Crate -Due to the wild-west nature of crates.io, we can't own a "bitwarden" namespace that we fully control. This means that certain crate names that we have a high likelihood of using in the future could be claimed by malicious or uniformed actors. +Due to the wild-west nature of crates.io, we can't own a "bitwarden" namespace that we fully +control. This means that certain crate names that we have a high likelihood of using in the future +could be claimed by malicious or uniformed actors. diff --git a/crates/bw/src/auth/login.rs b/crates/bw/src/auth/login.rs index d056f33a5..1c169817f 100644 --- a/crates/bw/src/auth/login.rs +++ b/crates/bw/src/auth/login.rs @@ -5,6 +5,7 @@ use bitwarden::{ }, Client, }; +use bitwarden_cli::text_prompt_when_none; use color_eyre::eyre::{bail, Result}; use inquire::{Password, Text}; use log::{debug, error, info}; @@ -14,11 +15,14 @@ pub(crate) async fn password_login(mut client: Client, email: Option) -> let password = Password::new("Password").without_confirmation().prompt()?; + let kdf = client.prelogin(email.clone()).await?; + let result = client .password_login(&PasswordLoginRequest { email: email.clone(), password: password.clone(), two_factor: None, + kdf: kdf.clone(), }) .await?; @@ -64,6 +68,7 @@ pub(crate) async fn password_login(mut client: Client, email: Option) -> email, password, two_factor, + kdf, }) .await?; @@ -97,14 +102,3 @@ pub(crate) async fn api_key_login( Ok(()) } - -/// Prompt the user for input if the value is None -/// -/// Typically used when the user can provide a value via CLI or prompt -fn text_prompt_when_none(prompt: &str, val: Option) -> Result { - Ok(if let Some(val) = val { - val - } else { - Text::new(prompt).prompt()? - }) -} diff --git a/crates/bw/src/main.rs b/crates/bw/src/main.rs index e5e7ccdb6..73e64a4aa 100644 --- a/crates/bw/src/main.rs +++ b/crates/bw/src/main.rs @@ -1,7 +1,10 @@ -use bitwarden::{client::client_settings::ClientSettings, tool::PasswordGeneratorRequest}; -use bitwarden_cli::{install_color_eyre, Color}; +use bitwarden::{ + auth::RegisterRequest, client::client_settings::ClientSettings, tool::PasswordGeneratorRequest, +}; +use bitwarden_cli::{install_color_eyre, text_prompt_when_none, Color}; use clap::{command, Args, CommandFactory, Parser, Subcommand}; use color_eyre::eyre::Result; +use inquire::Password; use render::Output; mod auth; @@ -25,6 +28,19 @@ struct Cli { enum Commands { Login(LoginArgs), + #[command(long_about = "Register")] + Register { + #[arg(short = 'e', long, help = "Email address")] + email: Option, + + name: Option, + + password_hint: Option, + + #[arg(short = 's', long, global = true, help = "Server URL")] + server: Option, + }, + #[command(long_about = "Manage vault items")] Item { #[command(subcommand)] @@ -115,25 +131,54 @@ async fn process_commands() -> Result<()> { return Ok(()); }; - if let Commands::Login(args) = command.clone() { - let settings = args.server.map(|server| ClientSettings { - api_url: format!("{}/api", server), - identity_url: format!("{}/identity", server), - ..Default::default() - }); - let client = bitwarden::Client::new(settings); - - match args.command { - // FIXME: Rust CLI will not support password login! - LoginCommands::Password { email } => { - auth::password_login(client, email).await?; + match command.clone() { + Commands::Login(args) => { + let settings = args.server.map(|server| ClientSettings { + api_url: format!("{}/api", server), + identity_url: format!("{}/identity", server), + ..Default::default() + }); + let client = bitwarden::Client::new(settings); + + match args.command { + // FIXME: Rust CLI will not support password login! + LoginCommands::Password { email } => { + auth::password_login(client, email).await?; + } + LoginCommands::ApiKey { + client_id, + client_secret, + } => auth::api_key_login(client, client_id, client_secret).await?, } - LoginCommands::ApiKey { - client_id, - client_secret, - } => auth::api_key_login(client, client_id, client_secret).await?, + return Ok(()); } - return Ok(()); + Commands::Register { + email, + name, + password_hint, + server, + } => { + let settings = server.map(|server| ClientSettings { + api_url: format!("{}/api", server), + identity_url: format!("{}/identity", server), + ..Default::default() + }); + let mut client = bitwarden::Client::new(settings); + + let email = text_prompt_when_none("Email", email)?; + let password = Password::new("Password").prompt()?; + + client + .auth() + .register(&RegisterRequest { + email, + name, + password, + password_hint, + }) + .await?; + } + _ => {} } // Not login, assuming we have a config @@ -142,6 +187,7 @@ async fn process_commands() -> Result<()> { // And finally we process all the commands which require authentication match command { Commands::Login(_) => unreachable!(), + Commands::Register { .. } => unreachable!(), Commands::Item { command: _ } => todo!(), Commands::Sync {} => todo!(), Commands::Generate { command } => match command { diff --git a/crates/bws/CHANGELOG.md b/crates/bws/CHANGELOG.md index 65568c6c2..15fcfdf3b 100644 --- a/crates/bws/CHANGELOG.md +++ b/crates/bws/CHANGELOG.md @@ -2,20 +2,23 @@ All notable changes to this project will be documented in this file. -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project +adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ### Added - Support for shell autocompletion with the `bws completions` command (#103) +- When running `bws` with no args, the help text is now printed to `stderr` instead of `stdout` to + be consistent with `bws subcommand` behavior (#190) ## [0.3.0] - 2023-07-26 ### Deprecated -- Switched command order from `action type` to `type action`, please re-read the help documentation (#76) +- Switched command order from `action type` to `type action`, please re-read the help documentation + (#76) ### Added diff --git a/crates/bws/Cargo.toml b/crates/bws/Cargo.toml index 48fadc605..ed4dc3b91 100644 --- a/crates/bws/Cargo.toml +++ b/crates/bws/Cargo.toml @@ -35,7 +35,7 @@ serde_yaml = "0.9" supports-color = "2.0.0" thiserror = "1.0.40" tokio = { version = "1.28.2", features = ["rt-multi-thread", "macros"] } -toml = "0.7.4" +toml = "0.8.0" uuid = { version = "^1.3.3", features = ["serde"] } bitwarden = { path = "../bitwarden", version = "0.3.0", features = ["secrets"] } diff --git a/crates/bws/README.md b/crates/bws/README.md index 0378e0dcf..11ea23814 100644 --- a/crates/bws/README.md +++ b/crates/bws/README.md @@ -1,8 +1,8 @@ # Bitwarden Secrets Manager CLI A Rust CLI for interacting with the -[Bitwarden Secrets Manager](https://bitwarden.com/products/secrets-manager/). This is a beta -release and might be missing some functionality. +[Bitwarden Secrets Manager](https://bitwarden.com/products/secrets-manager/). This is a beta release +and might be missing some functionality. ## Install diff --git a/crates/bws/src/main.rs b/crates/bws/src/main.rs index ab0c9afb2..5e6da19d8 100644 --- a/crates/bws/src/main.rs +++ b/crates/bws/src/main.rs @@ -246,7 +246,7 @@ async fn process_commands() -> Result<()> { let Some(command) = cli.command else { let mut cmd = Cli::command(); - cmd.print_help()?; + eprintln!("{}", cmd.render_help().ansi()); return Ok(()); }; diff --git a/crates/bws/src/render.rs b/crates/bws/src/render.rs index 7a7822113..4f2a32e9a 100644 --- a/crates/bws/src/render.rs +++ b/crates/bws/src/render.rs @@ -1,5 +1,5 @@ use bitwarden::secrets_manager::{projects::ProjectResponse, secrets::SecretResponse}; -use chrono::DateTime; +use chrono::{DateTime, Utc}; use clap::ValueEnum; use comfy_table::Table; use serde::Serialize; @@ -105,11 +105,8 @@ impl, const N: usize> TableSerialize for Vec { } } -fn format_date(date: &str) -> String { - DateTime::parse_from_rfc3339(date) - .unwrap() - .format("%Y-%m-%d %H:%M:%S") - .to_string() +fn format_date(date: &DateTime) -> String { + date.format("%Y-%m-%d %H:%M:%S").to_string() } impl TableSerialize<3> for ProjectResponse { diff --git a/languages/csharp/.editorconfig b/languages/csharp/.editorconfig new file mode 100644 index 000000000..21d7ac4a3 --- /dev/null +++ b/languages/csharp/.editorconfig @@ -0,0 +1,125 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Don't use tabs for indentation. +[*] +indent_style = space +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +guidelines = 120 +# (Please don't specify an indent_size here; that has too many unintended consequences.) + +# Code files +[*.{cs,csx,vb,vbx}] +indent_size = 4 +charset = utf-8-bom + +# Xml project files +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] +indent_size = 2 + +# Xml config files +[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] +indent_size = 2 + +# JSON files +[*.json] +indent_size = 2 + +# Dotnet code style settings: +[*.{cs,vb}] +# Sort using and Import directives with System.* appearing first +dotnet_sort_system_directives_first = true +# Avoid "this." and "Me." if not necessary +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_property = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_event = false:suggestion + +# Use language keywords instead of framework type names for type references +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# Suggest more modern language features when available +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion + +# Prefix private members with underscore +dotnet_naming_rule.private_members_with_underscore.symbols = private_fields +dotnet_naming_rule.private_members_with_underscore.style = prefix_underscore +dotnet_naming_rule.private_members_with_underscore.severity = suggestion + +dotnet_naming_symbols.private_fields.applicable_kinds = field +dotnet_naming_symbols.private_fields.applicable_accessibilities = private + +dotnet_naming_style.prefix_underscore.capitalization = camel_case +dotnet_naming_style.prefix_underscore.required_prefix = _ + +# Async methods should have "Async" suffix +dotnet_naming_rule.async_methods_end_in_async.symbols = any_async_methods +dotnet_naming_rule.async_methods_end_in_async.style = end_in_async +dotnet_naming_rule.async_methods_end_in_async.severity = suggestion + +dotnet_naming_symbols.any_async_methods.applicable_kinds = method +dotnet_naming_symbols.any_async_methods.applicable_accessibilities = * +dotnet_naming_symbols.any_async_methods.required_modifiers = async + +dotnet_naming_style.end_in_async.required_prefix = +dotnet_naming_style.end_in_async.required_suffix = Async +dotnet_naming_style.end_in_async.capitalization = pascal_case +dotnet_naming_style.end_in_async.word_separator = + +# Obsolete warnings, this should be removed or changed to warning once we address some of the obsolete items. +dotnet_diagnostic.CS0618.severity = suggestion + +# Obsolete warnings, this should be removed or changed to warning once we address some of the obsolete items. +dotnet_diagnostic.CS0612.severity = suggestion + +# Remove unnecessary using directives https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0005 +dotnet_diagnostic.IDE0005.severity = warning + +# CSharp code style settings: +[*.cs] +# Prefer "var" everywhere +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion +csharp_style_var_elsewhere = true:suggestion + +# Prefer method-like constructs to have a expression-body +csharp_style_expression_bodied_methods = true:none +csharp_style_expression_bodied_constructors = true:none +csharp_style_expression_bodied_operators = true:none + +# Prefer property-like constructs to have an expression-body +csharp_style_expression_bodied_properties = true:none +csharp_style_expression_bodied_indexers = true:none +csharp_style_expression_bodied_accessors = true:none + +# Suggest more modern language features when available +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion + +# Newline settings +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true + +# Namespace settings +csharp_style_namespace_declarations = file_scoped:warning + +# Switch expression +dotnet_diagnostic.CS8509.severity = error # missing switch case for named enum value +dotnet_diagnostic.CS8524.severity = none # missing switch case for unnamed enum value diff --git a/languages/csharp/Bitwarden.Sdk.Samples/Bitwarden.Sdk.Samples.csproj b/languages/csharp/Bitwarden.Sdk.Samples/Bitwarden.Sdk.Samples.csproj new file mode 100644 index 000000000..5b189d8ca --- /dev/null +++ b/languages/csharp/Bitwarden.Sdk.Samples/Bitwarden.Sdk.Samples.csproj @@ -0,0 +1,14 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + diff --git a/languages/csharp/Bitwarden.Sdk.Samples/Program.cs b/languages/csharp/Bitwarden.Sdk.Samples/Program.cs new file mode 100644 index 000000000..ee6834979 --- /dev/null +++ b/languages/csharp/Bitwarden.Sdk.Samples/Program.cs @@ -0,0 +1,30 @@ +using Bitwarden.Sdk; + +// Configure secrets +var accessToken = Environment.GetEnvironmentVariable("ACCESS_TOKEN")!; +var organizationIdString = Environment.GetEnvironmentVariable("ORGANIZATION_ID")!; +var organizationId = Guid.Parse(organizationIdString); + +// Create SDK Client +using var bitwardenClient = new BitwardenClient(); + +// Authenticate +bitwardenClient.AccessTokenLogin(accessToken); + +// Project operations +var projectResponse = bitwardenClient.Projects.Create(organizationId, "NewTestProject"); +var projectsResponse = bitwardenClient.Projects.List(organizationId); +var projectId = projectResponse.Id; +projectResponse = bitwardenClient.Projects.Get(projectId); +projectResponse = bitwardenClient.Projects.Update(projectId, organizationId, "NewTestProject2"); + +// Secret operations +var secretResponse = + bitwardenClient.Secrets.Create("key", "value", "note", organizationId, new[] { projectId }); +var secretId = secretResponse.Id; +var secretIdentifiersResponse = bitwardenClient.Secrets.List(organizationId); +secretResponse = bitwardenClient.Secrets.Get(secretId); +secretResponse = bitwardenClient.Secrets + .Update(secretId, "key2", "value2", "note2", organizationId, new[] { projectId }); +bitwardenClient.Secrets.Delete(new[] { secretId }); +bitwardenClient.Projects.Delete(new[] { projectId }); diff --git a/languages/csharp/Bitwarden.Sdk/Bitwarden.Sdk.csproj b/languages/csharp/Bitwarden.Sdk/Bitwarden.Sdk.csproj new file mode 100644 index 000000000..97f850211 --- /dev/null +++ b/languages/csharp/Bitwarden.Sdk/Bitwarden.Sdk.csproj @@ -0,0 +1,73 @@ + + + + net6.0 + README.md + enable + enable + Bitwarden.Sdk + Bitwarden Secrets Manager SDK + Bitwarden Inc. + .NET bindings for interacting with the Bitwarden Secrets Manager + Bitwarden Inc. + https://github.com/bitwarden/sdk/tree/master/languages/csharp + Bitwarden;Sdk;.NET + SDK + bitwarden.png + Git + Bitwarden.Sdk + https://bitwarden.com/products/secrets-manager/ + https://github.com/bitwarden/sdk/blob/master/LICENSE + + + + + + + + + + + + + + + 4 + + + + Always + true + + + Always + true + + + Always + true + + + + + Always + true + runtimes/osx-x64/native + + + Always + true + runtimes/osx-arm64/native + + + Always + true + runtimes/linux-x64/native + + + Always + true + runtimes/win-x64/native + + + diff --git a/languages/csharp/Bitwarden.Sdk/BitwardenAuthException.cs b/languages/csharp/Bitwarden.Sdk/BitwardenAuthException.cs new file mode 100644 index 000000000..7bb9f873a --- /dev/null +++ b/languages/csharp/Bitwarden.Sdk/BitwardenAuthException.cs @@ -0,0 +1,13 @@ +namespace Bitwarden.Sdk; + +public class BitwardenAuthException : Exception +{ + public BitwardenAuthException(string message) : base(message) + { + } + + public BitwardenAuthException(string message, Exception innerException) + : base(message, innerException) + { + } +} diff --git a/languages/csharp/Bitwarden.Sdk/BitwardenClient.cs b/languages/csharp/Bitwarden.Sdk/BitwardenClient.cs new file mode 100644 index 000000000..cb352d84f --- /dev/null +++ b/languages/csharp/Bitwarden.Sdk/BitwardenClient.cs @@ -0,0 +1,38 @@ +namespace Bitwarden.Sdk; + +public sealed class BitwardenClient : IDisposable +{ + private readonly CommandRunner _commandRunner; + private readonly BitwardenSafeHandle _handle; + + public BitwardenClient(BitwardenSettings? settings = null) + { + var clientSettings = new ClientSettings + { + ApiUrl = settings?.ApiUrl!, + IdentityUrl = settings?.IdentityUrl!, + UserAgent = "Bitwarden DOTNET-SDK" + }; + + _handle = BitwardenLibrary.Init(clientSettings.ToJson()); + _commandRunner = new CommandRunner(_handle); + Projects = new ProjectsClient(_commandRunner); + Secrets = new SecretsClient(_commandRunner); + } + + public void AccessTokenLogin(string accessToken) + { + var command = new Command { AccessTokenLogin = new AccessTokenLoginRequest { AccessToken = accessToken } }; + var response = _commandRunner.RunCommand(command); + if (response is not { Success: true }) + { + throw new BitwardenAuthException(response != null ? response.ErrorMessage : "Login failed"); + } + } + + public ProjectsClient Projects { get; } + + public SecretsClient Secrets { get; } + + public void Dispose() => _handle.Dispose(); +} diff --git a/languages/csharp/Bitwarden.Sdk/BitwardenException.cs b/languages/csharp/Bitwarden.Sdk/BitwardenException.cs new file mode 100644 index 000000000..1cf6abaa2 --- /dev/null +++ b/languages/csharp/Bitwarden.Sdk/BitwardenException.cs @@ -0,0 +1,13 @@ +namespace Bitwarden.Sdk; + +public class BitwardenException : Exception +{ + public BitwardenException(string message) : base(message) + { + } + + public BitwardenException(string message, Exception innerException) + : base(message, innerException) + { + } +} diff --git a/languages/csharp/Bitwarden.Sdk/BitwardenLibrary.cs b/languages/csharp/Bitwarden.Sdk/BitwardenLibrary.cs new file mode 100644 index 000000000..ada399401 --- /dev/null +++ b/languages/csharp/Bitwarden.Sdk/BitwardenLibrary.cs @@ -0,0 +1,21 @@ +using System.Runtime.InteropServices; + +namespace Bitwarden.Sdk; + +internal static class BitwardenLibrary +{ + [DllImport("bitwarden_c", CallingConvention = CallingConvention.Cdecl)] + private static extern BitwardenSafeHandle init(string settings); + + [DllImport("bitwarden_c", CallingConvention = CallingConvention.Cdecl)] + private static extern void free_mem(IntPtr handle); + + [DllImport("bitwarden_c", CallingConvention = CallingConvention.Cdecl)] + private static extern string run_command(string json, BitwardenSafeHandle handle); + + internal static BitwardenSafeHandle Init(string settings) => init(settings); + + internal static void FreeMemory(IntPtr handle) => free_mem(handle); + + internal static string RunCommand(string json, BitwardenSafeHandle handle) => run_command(json, handle); +} diff --git a/languages/csharp/Bitwarden.Sdk/BitwardenSafeHandle.cs b/languages/csharp/Bitwarden.Sdk/BitwardenSafeHandle.cs new file mode 100644 index 000000000..7939aab67 --- /dev/null +++ b/languages/csharp/Bitwarden.Sdk/BitwardenSafeHandle.cs @@ -0,0 +1,17 @@ +using Microsoft.Win32.SafeHandles; + +namespace Bitwarden.Sdk; + +internal class BitwardenSafeHandle : SafeHandleZeroOrMinusOneIsInvalid +{ + public BitwardenSafeHandle() : base(true) + { + SetHandle(handle); + } + + protected override bool ReleaseHandle() + { + BitwardenLibrary.FreeMemory(handle); + return true; + } +} diff --git a/languages/csharp/Bitwarden.Sdk/BitwardenSettings.cs b/languages/csharp/Bitwarden.Sdk/BitwardenSettings.cs new file mode 100644 index 000000000..a7ccdcd22 --- /dev/null +++ b/languages/csharp/Bitwarden.Sdk/BitwardenSettings.cs @@ -0,0 +1,14 @@ +namespace Bitwarden.Sdk; + +public class BitwardenSettings +{ + /// + /// The api url of the targeted Bitwarden instance. Defaults to `https://api.bitwarden.com` + /// + public string? ApiUrl { get; set; } + + /// + /// The identity url of the targeted Bitwarden instance. Defaults to `https://identity.bitwarden.com` + /// + public string? IdentityUrl { get; set; } +} diff --git a/languages/csharp/Bitwarden.Sdk/CommandRunner.cs b/languages/csharp/Bitwarden.Sdk/CommandRunner.cs new file mode 100644 index 000000000..fbd6b7e31 --- /dev/null +++ b/languages/csharp/Bitwarden.Sdk/CommandRunner.cs @@ -0,0 +1,20 @@ +using System.Text.Json; + +namespace Bitwarden.Sdk; + +internal class CommandRunner +{ + private readonly BitwardenSafeHandle _handle; + + internal CommandRunner(BitwardenSafeHandle handle) + { + _handle = handle; + } + + internal T? RunCommand(Command command) + { + var req = JsonSerializer.Serialize(command, Converter.Settings); + var result = BitwardenLibrary.RunCommand(req, _handle); + return JsonSerializer.Deserialize(result, Converter.Settings); + } +} diff --git a/languages/csharp/Bitwarden.Sdk/ProjectsClient.cs b/languages/csharp/Bitwarden.Sdk/ProjectsClient.cs new file mode 100644 index 000000000..54649b989 --- /dev/null +++ b/languages/csharp/Bitwarden.Sdk/ProjectsClient.cs @@ -0,0 +1,94 @@ +namespace Bitwarden.Sdk; + +public class ProjectsClient +{ + private readonly CommandRunner _commandRunner; + + internal ProjectsClient(CommandRunner commandRunner) + { + _commandRunner = commandRunner; + } + + public ProjectResponse Get(Guid id) + { + var command = new Command { Projects = new ProjectsCommand { Get = new ProjectGetRequest { Id = id } } }; + var result = _commandRunner.RunCommand(command); + + if (result is { Success: true }) + { + return result.Data; + } + + throw new BitwardenException(result != null ? result.ErrorMessage : "Project not found"); + } + + public ProjectResponse Create(Guid organizationId, string name) + { + var command = new Command + { + Projects = new ProjectsCommand + { + Create = new ProjectCreateRequest { OrganizationId = organizationId, Name = name } + } + }; + var result = _commandRunner.RunCommand(command); + + if (result is { Success: true }) + { + return result.Data; + } + + throw new BitwardenException(result != null ? result.ErrorMessage : "Project create failed"); + } + + public ProjectResponse Update(Guid id, Guid organizationId, string name) + { + var command = new Command + { + Projects = new ProjectsCommand + { + Update = new ProjectPutRequest { Id = id, OrganizationId = organizationId, Name = name } + } + }; + var result = _commandRunner.RunCommand(command); + + if (result is { Success: true }) + { + return result.Data; + } + + throw new BitwardenException(result != null ? result.ErrorMessage : "Project update failed"); + } + + public ProjectsDeleteResponse Delete(Guid[] ids) + { + var command = new Command + { + Projects = new ProjectsCommand { Delete = new ProjectsDeleteRequest { Ids = ids } } + }; + var result = _commandRunner.RunCommand(command); + + if (result is { Success: true }) + { + return result.Data; + } + + throw new BitwardenException(result != null ? result.ErrorMessage : "Project delete failed"); + } + + public ProjectsResponse List(Guid organizationId) + { + var command = new Command + { + Projects = new ProjectsCommand { List = new ProjectsListRequest { OrganizationId = organizationId } } + }; + var result = _commandRunner.RunCommand(command); + + if (result is { Success: true }) + { + return result.Data; + } + + throw new BitwardenException(result != null ? result.ErrorMessage : "No projects for given organization"); + } +} diff --git a/languages/csharp/Bitwarden.Sdk/SecretsClient.cs b/languages/csharp/Bitwarden.Sdk/SecretsClient.cs new file mode 100644 index 000000000..fafdca2ab --- /dev/null +++ b/languages/csharp/Bitwarden.Sdk/SecretsClient.cs @@ -0,0 +1,110 @@ +namespace Bitwarden.Sdk; + +public class SecretsClient +{ + private readonly CommandRunner _commandRunner; + + internal SecretsClient(CommandRunner commandRunner) + { + _commandRunner = commandRunner; + } + + public SecretResponse Get(Guid id) + { + var command = new Command { Secrets = new SecretsCommand { Get = new SecretGetRequest { Id = id } } }; + var result = _commandRunner.RunCommand(command); + + if (result is { Success: true }) + { + return result.Data; + } + + throw new BitwardenException(result != null ? result.ErrorMessage : "Secret not found"); + } + + public SecretResponse Create(string key, string value, string note, Guid organizationId, + Guid[] projectIds) + { + var command = new Command + { + Secrets = new SecretsCommand + { + Create = new SecretCreateRequest + { + Key = key, + Value = value, + Note = note, + OrganizationId = organizationId, + ProjectIds = projectIds + } + } + }; + + var result = _commandRunner.RunCommand(command); + + if (result is { Success: true }) + { + return result.Data; + } + + throw new BitwardenException(result != null ? result.ErrorMessage : "Secret create failed"); + } + + public SecretResponse Update(Guid id, string key, string value, string note, Guid organizationId, + Guid[] projectIds) + { + var command = new Command + { + Secrets = new SecretsCommand + { + Update = new SecretPutRequest + { + Id = id, + Key = key, + Value = value, + Note = note, + OrganizationId = organizationId, + ProjectIds = projectIds + } + } + }; + + var result = _commandRunner.RunCommand(command); + + if (result is { Success: true }) + { + return result.Data; + } + + throw new BitwardenException(result != null ? result.ErrorMessage : "Secret update failed"); + } + + public SecretsDeleteResponse Delete(Guid[] ids) + { + var command = new Command { Secrets = new SecretsCommand { Delete = new SecretsDeleteRequest { Ids = ids } } }; + var result = _commandRunner.RunCommand(command); + + if (result is { Success: true }) + { + return result.Data; + } + + throw new BitwardenException(result != null ? result.ErrorMessage : "Secrets delete failed"); + } + + public SecretIdentifiersResponse List(Guid organizationId) + { + var command = new Command + { + Secrets = new SecretsCommand { List = new SecretIdentifiersRequest { OrganizationId = organizationId } } + }; + var result = _commandRunner.RunCommand(command); + + if (result is { Success: true }) + { + return result.Data; + } + + throw new BitwardenException(result != null ? result.ErrorMessage : "No secrets for given organization"); + } +} diff --git a/languages/csharp/Bitwarden.Sdk/bitwarden.png b/languages/csharp/Bitwarden.Sdk/bitwarden.png new file mode 100644 index 000000000..681629a27 Binary files /dev/null and b/languages/csharp/Bitwarden.Sdk/bitwarden.png differ diff --git a/languages/csharp/Bitwarden.sln b/languages/csharp/Bitwarden.sln new file mode 100644 index 000000000..4cf8d147f --- /dev/null +++ b/languages/csharp/Bitwarden.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bitwarden.Sdk", "Bitwarden.Sdk\Bitwarden.Sdk.csproj", "{DADE59E5-E573-430A-8EB2-BC21D8E8C1D3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bitwarden.Sdk.Samples", "Bitwarden.Sdk.Samples\Bitwarden.Sdk.Samples.csproj", "{CA9F8EDC-643F-4624-AC00-F741E1F30CA4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DADE59E5-E573-430A-8EB2-BC21D8E8C1D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DADE59E5-E573-430A-8EB2-BC21D8E8C1D3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DADE59E5-E573-430A-8EB2-BC21D8E8C1D3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DADE59E5-E573-430A-8EB2-BC21D8E8C1D3}.Release|Any CPU.Build.0 = Release|Any CPU + {CA9F8EDC-643F-4624-AC00-F741E1F30CA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CA9F8EDC-643F-4624-AC00-F741E1F30CA4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CA9F8EDC-643F-4624-AC00-F741E1F30CA4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CA9F8EDC-643F-4624-AC00-F741E1F30CA4}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/languages/csharp/BitwardenSdk.cs b/languages/csharp/BitwardenSdk.cs deleted file mode 100644 index 286be75d5..000000000 --- a/languages/csharp/BitwardenSdk.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Bit.Sdk -{ - internal class BitwardenSdk : IDisposable - { - [DllImport("bitwarden_c", CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr init(string settings); - [DllImport("bitwarden_c", CallingConvention = CallingConvention.Cdecl)] - private static extern void free_mem(IntPtr clientPtr); - - [DllImport("bitwarden_c", CallingConvention = CallingConvention.Cdecl)] - private static extern string run_command(string loginRequest, IntPtr clientPtr); - - private readonly IntPtr _ptr; - private bool disposedValue; - - public BitwardenSdk(ClientSettings? settings = null) - { - _ptr = init(settings?.ToJson()); - } - - public ResponseForPasswordLoginResponse? PasswordLogin(string email, string password) => RunCommand(new Command - { - PasswordLogin = new PasswordLoginRequest { - Email = email, - Password = password - }, - }, ResponseForPasswordLoginResponse.FromJson); - - public ResponseForUserApiKeyResponse? UserApiKey(string password) => RunCommand(new Command - { - GetUserApiKey = new SecretVerificationRequest { - MasterPassword = password - }, - }, ResponseForUserApiKeyResponse.FromJson); - - private TReturn? RunCommand(Command input, Func deserializer) - { - var req = input.ToJson(); - - var json_result = run_command(req, _ptr); - Console.WriteLine(json_result); - return deserializer(json_result); - } - - protected virtual void Dispose(bool disposing) - { - if (!disposedValue) - { - if (disposing) - { - // TODO: dispose managed state (managed objects) - } - - free_mem(_ptr); - disposedValue = true; - } - } - - - ~BitwardenSdk() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: false); - } - - public void Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - } -} diff --git a/languages/csharp/LICENSE.txt b/languages/csharp/LICENSE.txt new file mode 100644 index 000000000..e9d496ff7 --- /dev/null +++ b/languages/csharp/LICENSE.txt @@ -0,0 +1,295 @@ +BITWARDEN SOFTWARE DEVELOPMENT KIT LICENSE AGREEMENT +Version 1, 17 March 2023 + +1. Introduction + +1.1 The Bitwarden Software Development Kit (referred to in the License Agreement +as the "SDK" and available for download at the following URL +https://github.com/bitwarden/sdk) is licensed to you subject to the terms of +this License Agreement. The License Agreement forms a legally binding contract +between you and the Company in relation to your use of the SDK. + +1.2 "Bitwarden" means the Bitwarden software made available by the Company, +available for download at the following URL, as updated from time to time. + +1.3 A "Compatible Application" means any software program or service that (i) +connects to and interoperates with a current version of the Bitwarden server +products distributed by the Company; and (ii) complies with the Company’s +acceptable use policy available at the following URL: +https://bitwarden.com/terms/#acceptable_use. + +1.4 "Company" means Bitwarden Inc., organized under the laws of the State of +Delaware. + +2. Accepting this License Agreement + +2.1 In order to access or use the SDK, you must first agree to the License +Agreement. You may not access or use the SDK if you do not accept the License +Agreement. + +2.2 By clicking to accept and/or accessing or using the SDK, you hereby agree to +the terms of the License Agreement. + +2.3 You may not access or use the SDK and may not accept the License Agreement +if you are a person barred from receiving the SDK under the laws of the United +States or other countries, including the country in which you are resident or +from which you access or use the SDK. + +2.4 If you are agreeing to be bound by the License Agreement on behalf of your +employer or any other entity, you represent and warrant that you have full legal +authority to bind your employer or such other entity to the License Agreement. +If you do not have the requisite authority, you may not accept the License +Agreement or you may not access or use the SDK on behalf of your employer or +other entity. + +3. SDK License from Bitwarden + +3.1 Subject to the terms of this License Agreement, Bitwarden grants you a +limited, worldwide, royalty-free, non-assignable, non-exclusive, and +non-sublicensable license to use the SDK solely (a) to develop, test, and +demonstrate a Compatible Application; (b) to develop, test, and run a Compatible +Application for personal use by your family; or (c) to to develop, test, and run +a Compatible Application for the internal business operations of your +organization in connection with a paid license for a Bitwarden server product, +provided that in no case above may the Compatible Application be offered, +licensed, or sold to a third party. + +3.2 You agree that Bitwarden or third parties own all legal right, title and +interest in and to the SDK, including any Intellectual Property Rights that +subsist in the SDK. "Intellectual Property Rights" means any and all rights +under patent law, copyright law, trade secret law, trademark law, and any and +all other proprietary rights. Bitwarden reserves all rights not expressly +granted to you. + +3.3 You may not use this SDK to develop applications for use with software other +than Bitwarden (including non-compatible implementations of Bitwarden) or to +develop another SDK. + +3.4 You may not use the SDK for any purpose not expressly permitted by the +License Agreement. Except for contributions to Bitwarden pursuant to the +Contribution License Agreement available at this URL: +https://cla-assistant.io/bitwarden/clients, or to the extent required by +applicable third party licenses, you may not copy modify, adapt, redistribute, +decompile, reverse engineer, disassemble, or create derivative works of the SDK +or any part of the SDK. + +3.5 Use, reproduction, and distribution of a component of the SDK licensed under +an open source software license are governed solely by the terms of that open +source software license and not the License Agreement. + +3.6 You agree that the form and nature of the SDK that the Company provides may +change without prior notice to you and that future versions of the SDK may be +incompatible with applications developed on previous versions of the SDK. You +agree that the Company may stop (permanently or temporarily) providing the SDK +or any features within the SDK to you or to users generally at the Company’s +sole discretion, without prior notice to you. + +3.7 Nothing in the License Agreement gives you a right to use any of the +Company’s trade names, trademarks, service marks, logos, domain names, or other +distinctive brand features. + +3.8 You agree that you will not remove, obscure, or alter any proprietary rights +notices (including copyright and trademark notices) that may be affixed to or +contained within the SDK. + +4. Use of the SDK by You + +4.1 The Company agrees that it obtains no right, title, or interest from you (or +your licensors) under the License Agreement in or to any software applications +that you develop using the SDK, including any Intellectual Property Rights that +subsist in those applications. + +4.2 You agree to use the SDK and write applications only for purposes that are +permitted by (a) the License Agreement and (b) any applicable law, regulation or +generally accepted practices or guidelines in the relevant jurisdictions +(including any laws regarding the export of data or software to and from the +United States or other relevant countries). + +4.3 You agree that if you use the SDK to develop applications for other users, +you will protect the privacy and legal rights of those users. If the users +provide you with user names, passwords, or other login information or personal +information, you must make the users aware that the information will be +available to your application, and you must provide legally adequate privacy +notice and protection for those users. If your application stores personal or +sensitive information provided by users, it must do so securely. If the user +provides your application with Bitwarden Account information, your application +may only use that information to access the user's Bitwarden Account when, and +for the limited purposes for which, the user has given you permission to do so. + +4.4 You agree that you will not engage in any activity with the SDK, including +the development or distribution of an application, that interferes with, +disrupts, damages, or accesses in an unauthorized manner the servers, networks, +or other properties or services of any third party including, but not limited +to, the Company, or any mobile communications carrier or public cloud service. + +4.5 If you use the SDK to retrieve a user's data from Bitwarden, you acknowledge +and agree that you shall retrieve data only with the user's explicit consent and +only when, and for the limited purposes for which, the user has given you +permission to do so. + +4.6 You agree that you are solely responsible for, and that the Company has no +responsibility to you or to any third party for, any data, content, or resources +that you create, transmit or display through Bitwarden and/or applications for +Bitwarden, and for the consequences of your actions (including any loss or +damage which Bitwarden may suffer) by doing so. + +4.7 You agree that you are solely responsible for, and that the Company has no +responsibility to you or to any third party for, any breach of your obligations +under the License Agreement, any applicable third party contract or Terms of +Service, or any applicable law or regulation, and for the consequences +(including any loss or damage which the Company or any third party may suffer) +of any such breach. + +5. Third Party Applications + +5.1 If you use the SDK to integrate or run applications developed by a third +party or that access data, content or resources provided by a third party, you +agree that the Company is not responsible for those applications, data, content, +or resources. You understand that all data, content or resources which you may +access through such third party applications are the sole responsibility of the +person from which they originated and that the Company is not liable for any +loss or damage that you may experience as a result of the use or access of any +of those third party applications, data, content, or resources. + +5.2 You should be aware that the data, content, and resources presented to you +through such a third party application may be protected by intellectual property +rights which are owned by the providers (or by other persons or companies on +their behalf). You acknowledge that your use of such third party applications, +data, content, or resources may be subject to separate terms between you and the +relevant third party. In that case, the License Agreement does not affect your +legal relationship with these third parties. + +6. Use of Bitwarden Server + +You acknowledge and agree that the Bitwarden server products to which any +Compatible Application must connect is protected by intellectual property rights +which are owned by the Company and your use of the Bitwarden server products is +subject to additional terms not set forth in this License Agreement. + +7. Terminating this License Agreement + +7.1 The License Agreement will continue to apply until terminated by either you +or the Company as set out below. + +7.2 If you want to terminate the License Agreement, you may do so by ceasing +your use of the SDK and any relevant developer credentials. + +7.3 The Company may at any time, terminate the License Agreement with you if: + +(a) you have breached any provision of the License Agreement; or + +(b) the Company is required to do so by law; or + +(c) a third party with whom the Company offered certain parts of the SDK to you +has terminated its relationship with the Company or ceased to offer certain +parts of the SDK to either the Company or to you; or + +(d) the Company decides to no longer provide the SDK or certain parts of the SDK +to users in the country in which you are resident or from which you use the +service, or the provision of the SDK or certain SDK services to you by the +Company is, in the Company’'s sole discretion, no longer commercially viable or +technically practicable. + +7.4 When the License Agreement comes to an end, all of the legal rights, +obligations and liabilities that you and the Company have benefited from, been +subject to (or which have accrued over time whilst the License Agreement has +been in force) or which are expressed to continue indefinitely, shall be +unaffected by this cessation, and the provisions of paragraph 12.8 shall +continue to apply to such rights, obligations and liabilities indefinitely. + +8. NO SUPPORT + +The Company is not obligated under this License Agreement to provide you any +support services for the SDK. Any support provided is at the Company’s sole +discretion and provided on an "as is" basis and without warranty of any kind. + +9. DISCLAIMER OF WARRANTIES + +9.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT YOUR USE OF THE SDK IS AT YOUR SOLE +RISK AND THAT THE SDK IS PROVIDED "AS IS" AND "AS AVAILABLE" WITHOUT WARRANTY OF +ANY KIND FROM Bitwarden. + +9.2 YOUR USE OF THE SDK AND ANY MATERIAL DOWNLOADED OR OTHERWISE OBTAINED +THROUGH THE USE OF THE SDK IS AT YOUR OWN DISCRETION AND RISK AND YOU ARE SOLELY +RESPONSIBLE FOR ANY DAMAGE TO YOUR COMPUTER SYSTEM OR OTHER DEVICE OR LOSS OF +DATA THAT RESULTS FROM SUCH USE. + +9.3 THE COMPANY FURTHER EXPRESSLY DISCLAIMS ALL WARRANTIES AND CONDITIONS OF ANY +KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE IMPLIED +WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE +AND NON-INFRINGEMENT. + +10. LIMITATION OF LIABILITY + +YOU EXPRESSLY UNDERSTAND AND AGREE THAT THE COMPANY, ITS SUBSIDIARIES AND +AFFILIATES, AND ITS LICENSORS SHALL NOT BE LIABLE TO YOU UNDER ANY THEORY OF +LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, +STATUTORY, OR EXEMPLARY DAMAGES THAT MAY BE INCURRED BY YOU, INCLUDING ANY LOSS +OF DATA, WHETHER OR NOT THE COMPANY OR ITS REPRESENTATIVES HAVE BEEN ADVISED OF +OR SHOULD HAVE BEEN AWARE OF THE POSSIBILITY OF ANY SUCH LOSSES ARISING. + +11. Indemnification + +To the maximum extent permitted by law, you agree to defend, indemnify and hold +harmless the Company, its affiliates and their respective directors, officers, +employees and agents from and against any and all claims, actions, suits or +proceedings, as well as any and all losses, liabilities, damages, costs and +expenses (including reasonable attorneys fees) arising out of or accruing from +(a) your use of the SDK, (b) any application you develop on the SDK that +infringes any copyright, trademark, trade secret, trade dress, patent or other +intellectual property right of any person or defames any person or violates +their rights of publicity or privacy, and (c) any non-compliance by you with the +License Agreement. + +12. General Legal Terms + +12.1 The Company may make changes to the License Agreement as it distributes new +versions of the SDK. When these changes are made, the Company will make a new +version of the License Agreement available on the website where the SDK is made +available. + +12.2 The License Agreement constitutes the whole legal agreement between you and +the Company and governs your use of the SDK (excluding any services or software +which the Company may provide to you under a separate written agreement), and +completely replaces any prior agreements between you and the Company in relation +to the SDK. + +12.3 You agree that if the Company does not exercise or enforce any legal right +or remedy which is contained in the License Agreement (or which the Company has +the benefit of under any applicable law), this will not be taken to be a formal +waiver of the Company's rights and that those rights or remedies will still be +available to the Company. + +12.4 If any court of law, having the jurisdiction to decide on this matter, +rules that any provision of the License Agreement is invalid, then that +provision will be removed from the License Agreement without affecting the rest +of the License Agreement. The remaining provisions of the License Agreement will +continue to be valid and enforceable. + +12.5 You acknowledge and agree that each member of the group of companies of +which the Company is the parent shall be third party beneficiaries to the +License Agreement and that such other companies shall be entitled to directly +enforce, and rely upon, any provision of the License Agreement that confers a +benefit on them or rights in favor of them. Other than this, no other person or +company shall be third party beneficiaries to the License Agreement. + +12.6 EXPORT RESTRICTIONS. THE SDK IS SUBJECT TO UNITED STATES EXPORT LAWS AND +REGULATIONS. YOU MUST COMPLY WITH ALL DOMESTIC AND INTERNATIONAL EXPORT LAWS AND +REGULATIONS THAT APPLY TO THE SDK. THESE LAWS INCLUDE RESTRICTIONS ON +DESTINATIONS, END USERS, AND END USE. + +12.7 The rights granted in the License Agreement may not be assigned or +transferred by either you or the Company without the prior written approval of +the other party, provided that the Company may assign this License Agreement +upon notice to you in connection with an acquisition, merger, sale of assets, or +similar corporate change in control for the Company or the Intellectual Property +Rights in the SDK. + +12.8 The License Agreement, and any dispute relating to or arising out of this +License Agreement, shall be governed by the laws of the State of California +without regard to its conflict of laws provisions. You and the Company agree to +submit to the exclusive jurisdiction of the courts located within the county of +Los Angeles, California to resolve any dispute or legal matter arising from the +License Agreement. Notwithstanding this, you agree that the Company shall be +allowed to apply for injunctive remedies, or any equivalent type of urgent legal +relief, in any forum or jurisdiction. diff --git a/languages/csharp/Program.cs b/languages/csharp/Program.cs deleted file mode 100644 index 359454e32..000000000 --- a/languages/csharp/Program.cs +++ /dev/null @@ -1,6 +0,0 @@ -using Bit.Sdk; - -var sdk = new BitwardenSdk(); -sdk.PasswordLogin("test@bitwarden.com", "asdfasdf"); -var apiKey = sdk.UserApiKey("asdfasdf"); -Console.WriteLine(apiKey?.Data?.ApiKey ?? "api key was null"); diff --git a/languages/csharp/README.md b/languages/csharp/README.md index e390a1ca1..dea2d1d22 100644 --- a/languages/csharp/README.md +++ b/languages/csharp/README.md @@ -1,17 +1,70 @@ -# Requirements +# Bitwarden Secrets Manager SDK -- Dotnet +.NET bindings for interacting with the [Bitwarden Secrets Manager]. This is a beta release and might be missing some functionality. -# Installation +## Create access token -From the `languages/csharp/` directory, +Review the help documentation on [Access Tokens] -```bash -dotnet restore +## Usage code snippets + +### Create new Bitwarden client + +```csharp +const string accessToken = ""; +using var bitwardenClient = new BitwardenClient(); +bitwardenClient.AccessTokenLogin(accessToken); +``` + +### Create new project + +```csharp +var organizationId = Guid.Parse(""); +var projectResponse = bitwardenClient.Projects().Create(organizationId, "TestProject"); +``` + +### List all projects + +```csharp +var response = bitwardenClient.Projects.List(organizationId); +``` + +### Update project + +```csharp +var projectId = projectResponse.Id; +projectResponse = bitwardenClient.Projects.Get(projectId); +projectResponse = bitwardenClient.Projects.Update(projectId, organizationId, "TestProjectUpdated"); +``` + +### Add new secret + +```csharp +var key = "key"; +var value = "value"; +var note = "note"; +var secretResponse = bitwardenClient.Secrets.Create(key, value, note, organizationId, new[] { projectId }); +var secretId = secretResponse.Id; +``` + +### Update secret +```csharp +secretResponse = bitwardenClient.Secrets + .Update(secretId, "key2", "value2", "note2", organizationId, new[] { projectId }); ``` -# Run +### List secrets -```bash -dotnet run +```csharp +var secretIdentifiersResponse = bitwardenClient.Secrets.List(organizationId); ``` + +# Delete secret or project + +```csharp +bitwardenClient.Secrets.Delete(new [] { secretId }); +bitwardenClient.Projects.Delete(new [] { projectId }); +``` + +[Access Tokens]: https://bitwarden.com/help/access-tokens/ +[Bitwarden Secrets Manager]: https://bitwarden.com/products/secrets-manager/ diff --git a/languages/csharp/bitwardenSdk.csproj b/languages/csharp/bitwardenSdk.csproj deleted file mode 100644 index 10e89212e..000000000 --- a/languages/csharp/bitwardenSdk.csproj +++ /dev/null @@ -1,41 +0,0 @@ - - - - Exe - net6.0 - enable - enable - - - - - - - - - - - - - Always - - - Always - - - Always - - - - - - Always - - - Always - - - Always - - - diff --git a/languages/csharp/global.json b/languages/csharp/global.json new file mode 100644 index 000000000..527fd31d3 --- /dev/null +++ b/languages/csharp/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "6.0.413", + "rollForward": "latestFeature" + } +} diff --git a/languages/js_webassembly/package-lock.json b/languages/js_webassembly/package-lock.json index ad09cd303..81c910b90 100644 --- a/languages/js_webassembly/package-lock.json +++ b/languages/js_webassembly/package-lock.json @@ -5,13 +5,13 @@ "packages": { "": { "devDependencies": { - "html-webpack-plugin": "5.5.1", + "html-webpack-plugin": "5.5.3", "text-encoding": "0.7.0", - "ts-loader": "9.4.2", - "wasm-pack": "0.11.0", - "webpack": "5.81.0", - "webpack-cli": "5.0.2", - "webpack-dev-server": "4.13.3" + "ts-loader": "9.4.4", + "wasm-pack": "0.12.1", + "webpack": "5.88.2", + "webpack-cli": "5.1.4", + "webpack-dev-server": "4.15.1" } }, "node_modules/@discoveryjs/json-ext": { @@ -88,9 +88,9 @@ "dev": true }, "node_modules/@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "version": "1.19.3", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.3.tgz", + "integrity": "sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==", "dev": true, "dependencies": { "@types/connect": "*", @@ -98,27 +98,27 @@ } }, "node_modules/@types/bonjour": { - "version": "3.5.10", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", - "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "version": "3.5.11", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.11.tgz", + "integrity": "sha512-isGhjmBtLIxdHBDl2xGwUzEM8AOyOvWsADWq7rqirdi/ZQoHnLWErHvsThcEzTX8juDRiZtzp2Qkv5bgNh6mAg==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "version": "3.4.36", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz", + "integrity": "sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/connect-history-api-fallback": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", - "integrity": "sha512-4x5FkPpLipqwthjPsF7ZRbOv3uoLUFkTA9G9v583qi4pACvq0uTELrB8OLUzPWUI4IJIyvM85vzkV1nyiI2Lig==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.1.tgz", + "integrity": "sha512-iaQslNbARe8fctL5Lk+DsmgWOM83lM+7FzP0eQUJs1jd3kBE8NWqBTIT2S8SqQOJjxvt2eyIjpOuYeRXq2AdMw==", "dev": true, "dependencies": { "@types/express-serve-static-core": "*", @@ -126,9 +126,9 @@ } }, "node_modules/@types/eslint": { - "version": "8.44.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.2.tgz", - "integrity": "sha512-sdPRb9K6iL5XZOmBubg8yiFp5yS/JdUDQsq5e6h95km91MCYMuvp7mh1fjPEYUhvHepKpZOjnEaMBR4PxjWDzg==", + "version": "8.44.3", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.3.tgz", + "integrity": "sha512-iM/WfkwAhwmPff3wZuPLYiHX18HI24jU8k1ZSH7P8FHwxTjZ2P6CoX2wnF43oprR+YXJM6UUxATkNvyv/JHd+g==", "dev": true, "dependencies": { "@types/estree": "*", @@ -136,9 +136,9 @@ } }, "node_modules/@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "version": "3.7.5", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.5.tgz", + "integrity": "sha512-JNvhIEyxVW6EoMIFIvj93ZOywYFatlpu9deeH6eSx6PE3WHYvHaQtmHmQeNw7aA81bYGBPPQqdtBm6b1SsQMmA==", "dev": true, "dependencies": { "@types/eslint": "*", @@ -146,15 +146,15 @@ } }, "node_modules/@types/estree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", - "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.2.tgz", + "integrity": "sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==", "dev": true }, "node_modules/@types/express": { - "version": "4.17.17", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", - "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", + "version": "4.17.18", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.18.tgz", + "integrity": "sha512-Sxv8BSLLgsBYmcnGdGjjEjqET2U+AKAdCRODmMiq02FgjwuV75Ut85DRpvFjyw/Mk0vgUOliGRU0UUmuuZHByQ==", "dev": true, "dependencies": { "@types/body-parser": "*", @@ -164,9 +164,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.17.35", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz", - "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==", + "version": "4.17.37", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.37.tgz", + "integrity": "sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==", "dev": true, "dependencies": { "@types/node": "*", @@ -182,24 +182,24 @@ "dev": true }, "node_modules/@types/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.2.tgz", + "integrity": "sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==", "dev": true }, "node_modules/@types/http-proxy": { - "version": "1.17.11", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.11.tgz", - "integrity": "sha512-HC8G7c1WmaF2ekqpnFq626xd3Zz0uvaqFmBJNRZCGEZCXkvSdJoNFn/8Ygbd9fKNQj8UzLdCETaI0UWPAjK7IA==", + "version": "1.17.12", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.12.tgz", + "integrity": "sha512-kQtujO08dVtQ2wXAuSFfk9ASy3sug4+ogFR8Kd8UgP8PEuc1/G/8yjYRmp//PcDNJEUKOza/MrQu15bouEUCiw==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", + "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==", "dev": true }, "node_modules/@types/mime": { @@ -209,15 +209,15 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.5.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.1.tgz", - "integrity": "sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg==", + "version": "20.6.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.5.tgz", + "integrity": "sha512-2qGq5LAOTh9izcc0+F+dToFigBWiK1phKPt7rNhOqJSr35y8rlIBjDwGtFSgAI6MGIhjwOVNSQZVdJsZJ2uR1w==", "dev": true }, "node_modules/@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "version": "6.9.8", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.8.tgz", + "integrity": "sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==", "dev": true }, "node_modules/@types/range-parser": { @@ -753,9 +753,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.10", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", - "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "version": "4.21.11", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.11.tgz", + "integrity": "sha512-xn1UXOKUz7DjdGlg9RrUr0GGiWzI97UQJnugHtH0OLDfJB7jMgoIkYvRIEO1l9EeEERVqeqLYOcFBW9ldjypbQ==", "dev": true, "funding": [ { @@ -772,10 +772,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001517", - "electron-to-chromium": "^1.4.477", + "caniuse-lite": "^1.0.30001538", + "electron-to-chromium": "^1.4.526", "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.11" + "update-browserslist-db": "^1.0.13" }, "bin": { "browserslist": "cli.js" @@ -823,9 +823,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001522", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001522.tgz", - "integrity": "sha512-TKiyTVZxJGhsTszLuzb+6vUZSjVOAhClszBr2Ta2k9IwtNBT/4dzmL6aywt0HCgEZlmwJzXJd8yNiob6HgwTRg==", + "version": "1.0.30001538", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001538.tgz", + "integrity": "sha512-HWJnhnID+0YMtGlzcp3T9drmBJUVDchPJ08tpUGFLs9CYlwWPH2uLgpHn8fND5pCgXVtnGS3H4QR9XLMHVNkHw==", "dev": true, "funding": [ { @@ -1159,9 +1159,9 @@ "dev": true }, "node_modules/dns-packet": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.0.tgz", - "integrity": "sha512-rza3UH1LwdHh9qyPXp8lkwpjSNk/AMD3dPytUoRoqnypDUhY0xvbdmVhWOfxO68frEfV9BU8V12Ez7ZsHGZpCQ==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", "dev": true, "dependencies": { "@leichtgewicht/ip-codec": "^2.0.1" @@ -1251,9 +1251,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.496", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.496.tgz", - "integrity": "sha512-qeXC3Zbykq44RCrBa4kr8v/dWzYJA8rAwpyh9Qd+NKWoJfjG5vvJqy9XOJ9H4P/lqulZBCgUWAYi+FeK5AuJ8g==", + "version": "1.4.528", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.528.tgz", + "integrity": "sha512-UdREXMXzLkREF4jA8t89FQjA8WHI6ssP38PMY4/4KhXFQbtImnghh4GkCgrtiZwLKUKVD2iTVXvDVQjfomEQuA==", "dev": true }, "node_modules/encodeurl": { @@ -1300,9 +1300,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz", - "integrity": "sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.1.tgz", + "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==", "dev": true }, "node_modules/escalade": { @@ -1535,9 +1535,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", "dev": true, "funding": [ { @@ -1609,9 +1609,9 @@ "dev": true }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, "optional": true, @@ -1839,9 +1839,9 @@ } }, "node_modules/html-webpack-plugin": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.1.tgz", - "integrity": "sha512-cTUzZ1+NqjGEKjmVgZKLMdiFg3m9MdRXkZW2OEe69WYVi5ONLMmlnSZdXzGGMOq0C8jGDrL6EWyEDDUioHO/pA==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.3.tgz", + "integrity": "sha512-6YrDKTuqaP/TquFH7h4srYWsZx+x6k6+FbsTm0ziCwGHDP78Unr1r9F/H4+sGmMbX08GQcJ+K64x55b+7VM/jg==", "dev": true, "dependencies": { "@types/html-minifier-terser": "^6.0.0", @@ -2923,9 +2923,9 @@ "dev": true }, "node_modules/resolve": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", - "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", + "version": "1.22.6", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz", + "integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==", "dev": true, "dependencies": { "is-core-module": "^2.13.0", @@ -3425,9 +3425,9 @@ } }, "node_modules/tar": { - "version": "6.1.15", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.15.tgz", - "integrity": "sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", + "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", "dev": true, "dependencies": { "chownr": "^2.0.0", @@ -3442,9 +3442,9 @@ } }, "node_modules/terser": { - "version": "5.19.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.2.tgz", - "integrity": "sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.20.0.tgz", + "integrity": "sha512-e56ETryaQDyebBwJIWYB2TT6f2EZ0fL0sW/JRXNMN26zZdKi2u/E/5my5lG6jNxym6qsrVXfFRmOdV42zlAgLQ==", "dev": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -3534,9 +3534,9 @@ } }, "node_modules/ts-loader": { - "version": "9.4.2", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.2.tgz", - "integrity": "sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA==", + "version": "9.4.4", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.4.tgz", + "integrity": "sha512-MLukxDHBl8OJ5Dk3y69IsKVFRA/6MwzEqBgh+OXMPB/OD01KQuWPFd1WAQP8a5PeSCAxfnkhiuWqfmFJzJQt9w==", "dev": true, "dependencies": { "chalk": "^4.1.0", @@ -3572,9 +3572,9 @@ } }, "node_modules/typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true, "peer": true, "bin": { @@ -3595,9 +3595,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "dev": true, "funding": [ { @@ -3673,16 +3673,16 @@ } }, "node_modules/wasm-pack": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/wasm-pack/-/wasm-pack-0.11.0.tgz", - "integrity": "sha512-PB/4QcPNo02d3cyfxxw/T8RvB9G3uMMfKT1cD028+NaC8Gnoqz8qfRQa446vgneK/4V5FUZY9Nvoi/lVI1/wKw==", + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/wasm-pack/-/wasm-pack-0.12.1.tgz", + "integrity": "sha512-dIyKWUumPFsGohdndZjDXRFaokUT/kQS+SavbbiXVAvA/eN4riX5QNdB6AhXQx37zNxluxQkuixZUgJ8adKjOg==", "dev": true, "hasInstallScript": true, "dependencies": { "binary-install": "^1.0.1" }, "bin": { - "wasm-pack": "node ./run.js" + "wasm-pack": "run.js" } }, "node_modules/watchpack": { @@ -3708,9 +3708,9 @@ } }, "node_modules/webpack": { - "version": "5.81.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.81.0.tgz", - "integrity": "sha512-AAjaJ9S4hYCVODKLQTgG5p5e11hiMawBwV2v8MYLE0C/6UAGLuAF4n1qa9GOwdxnicaP+5k6M5HrLmD4+gIB8Q==", + "version": "5.88.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", + "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.3", @@ -3719,10 +3719,10 @@ "@webassemblyjs/wasm-edit": "^1.11.5", "@webassemblyjs/wasm-parser": "^1.11.5", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", + "acorn-import-assertions": "^1.9.0", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.13.0", + "enhanced-resolve": "^5.15.0", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -3732,7 +3732,7 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.2", + "schema-utils": "^3.2.0", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.3.7", "watchpack": "^2.4.0", @@ -3755,15 +3755,15 @@ } }, "node_modules/webpack-cli": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.0.2.tgz", - "integrity": "sha512-4y3W5Dawri5+8dXm3+diW6Mn1Ya+Dei6eEVAdIduAmYNLzv1koKVAqsfgrrc9P2mhrYHQphx5htnGkcNwtubyQ==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", + "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", "dev": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^2.0.1", - "@webpack-cli/info": "^2.0.1", - "@webpack-cli/serve": "^2.0.2", + "@webpack-cli/configtest": "^2.1.1", + "@webpack-cli/info": "^2.0.2", + "@webpack-cli/serve": "^2.0.5", "colorette": "^2.0.14", "commander": "^10.0.1", "cross-spawn": "^7.0.3", @@ -3885,9 +3885,9 @@ } }, "node_modules/webpack-dev-server": { - "version": "4.13.3", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.13.3.tgz", - "integrity": "sha512-KqqzrzMRSRy5ePz10VhjyL27K2dxqwXQLP5rAKwRJBPUahe7Z2bBWzHw37jeb8GCPKxZRO79ZdQUAPesMh/Nug==", + "version": "4.15.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz", + "integrity": "sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==", "dev": true, "dependencies": { "@types/bonjour": "^3.5.9", @@ -3896,7 +3896,7 @@ "@types/serve-index": "^1.9.1", "@types/serve-static": "^1.13.10", "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.1", + "@types/ws": "^8.5.5", "ansi-html-community": "^0.0.8", "bonjour-service": "^1.0.11", "chokidar": "^3.5.3", @@ -4069,9 +4069,9 @@ "dev": true }, "node_modules/ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", + "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", "dev": true, "engines": { "node": ">=10.0.0" diff --git a/languages/js_webassembly/package.json b/languages/js_webassembly/package.json index cad7e3b98..602cbc7a4 100644 --- a/languages/js_webassembly/package.json +++ b/languages/js_webassembly/package.json @@ -4,12 +4,12 @@ "build:watch": "npm run build && webpack serve" }, "devDependencies": { - "html-webpack-plugin": "5.5.1", + "html-webpack-plugin": "5.5.3", "text-encoding": "0.7.0", - "ts-loader": "9.4.2", - "wasm-pack": "0.11.0", - "webpack": "5.81.0", - "webpack-cli": "5.0.2", - "webpack-dev-server": "4.13.3" + "ts-loader": "9.4.4", + "wasm-pack": "0.12.1", + "webpack": "5.88.2", + "webpack-cli": "5.1.4", + "webpack-dev-server": "4.15.1" } } diff --git a/languages/kotlin/app/build.gradle b/languages/kotlin/app/build.gradle index 2b7bc0255..709e32889 100644 --- a/languages/kotlin/app/build.gradle +++ b/languages/kotlin/app/build.gradle @@ -48,7 +48,7 @@ android { } dependencies { - // implementation 'com.bitwarden.sdk' + // implementation 'com.bitwarden.sdk-android' implementation project(':sdk') implementation 'androidx.core:core-ktx:1.8.0' diff --git a/languages/kotlin/doc.md b/languages/kotlin/doc.md index 9cda1386e..9e8c19929 100644 --- a/languages/kotlin/doc.md +++ b/languages/kotlin/doc.md @@ -1,8 +1,8 @@ # Bitwarden Mobile SDK -Auto generated documentation for the Bitwarden Mobile SDK. For more information please refer to -the rust crates `bitwarden` and `bitwarden-uniffi`. For code samples check the -`languages/kotlin/app` and `languages/swift/app` directories. +Auto generated documentation for the Bitwarden Mobile SDK. For more information please refer to the +rust crates `bitwarden` and `bitwarden-uniffi`. For code samples check the `languages/kotlin/app` +and `languages/swift/app` directories. ## Client @@ -108,6 +108,19 @@ Hash the user password **Output**: std::result::Result +### `make_register_keys` + +Generate keys needed for registration process + +**Arguments**: + +- self: +- email: String +- password: String +- kdf: [Kdf](#kdf) + +**Output**: std::result::Result + ## ClientCiphers ### `encrypt` @@ -180,6 +193,34 @@ Initialization method for the crypto. Needs to be called before any other crypto **Output**: std::result::Result<,BitwardenError> +## ClientExporters + +### `export_vault` + +**API Draft:** Export user vault + +**Arguments**: + +- self: +- folders: Vec +- ciphers: Vec +- format: [ExportFormat](#exportformat) + +**Output**: std::result::Result + +### `export_organization_vault` + +**API Draft:** Export organization vault + +**Arguments**: + +- self: +- collections: Vec +- ciphers: Vec +- format: [ExportFormat](#exportformat) + +**Output**: std::result::Result + ## ClientFolders ### `encrypt` @@ -263,6 +304,91 @@ Decrypt password history **Output**: std::result::Result +## ClientSends + +### `encrypt` + +Encrypt send + +**Arguments**: + +- self: +- send: [SendView](#sendview) + +**Output**: std::result::Result + +### `encrypt_buffer` + +Encrypt a send file in memory + +**Arguments**: + +- self: +- send: [Send](#send) +- buffer: Vec<> + +**Output**: std::result::Result + +### `encrypt_file` + +Encrypt a send file located in the file system + +**Arguments**: + +- self: +- send: [Send](#send) +- decrypted_file_path: String +- encrypted_file_path: String + +**Output**: std::result::Result<,BitwardenError> + +### `decrypt` + +Decrypt send + +**Arguments**: + +- self: +- send: [Send](#send) + +**Output**: std::result::Result + +### `decrypt_list` + +Decrypt send list + +**Arguments**: + +- self: +- sends: Vec + +**Output**: std::result::Result + +### `decrypt_buffer` + +Decrypt a send file in memory + +**Arguments**: + +- self: +- send: [Send](#send) +- buffer: Vec<> + +**Output**: std::result::Result + +### `decrypt_file` + +Decrypt a send file located in the file system + +**Arguments**: + +- self: +- send: [Send](#send) +- encrypted_file_path: String +- decrypted_file_path: String + +**Output**: std::result::Result<,BitwardenError> + ## ClientVault ### `folders` @@ -297,7 +423,7 @@ Ciphers operations ### `password_history` -Ciphers operations +Password history operations **Arguments**: @@ -305,6 +431,16 @@ Ciphers operations **Output**: Arc +### `sends` + +Sends operations + +**Arguments**: + +- self: Arc + +**Output**: Arc + # References References are generated from the JSON schemas and should mostly match the kotlin and swift @@ -600,6 +736,37 @@ implementations. +## `ExportFormat` + + + + + + + + + + + + + + + +
KeyTypeDescription
EncryptedJsonobject
+ + + + + + + + + + + +
KeyTypeDescription
passwordstring
+
+ ## `Folder` @@ -906,3 +1073,183 @@ implementations.
+ +## `Send` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyTypeDescription
idstring
accessIdstring
name
notes
key
passwordstring,null
type
file
text
maxAccessCountinteger,null
accessCountinteger
disabledboolean
hideEmailboolean
revisionDatestring
deletionDatestring
expirationDatestring,null
+ +## `SendView` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyTypeDescription
idstring
accessIdstring
namestring
notesstring,null
key
passwordstring,null
type
file
text
maxAccessCountinteger,null
accessCountinteger
disabledboolean
hideEmailboolean
revisionDatestring
deletionDatestring
expirationDatestring,null
diff --git a/languages/kotlin/sdk/build.gradle b/languages/kotlin/sdk/build.gradle index 3d164e094..3fb6956e2 100644 --- a/languages/kotlin/sdk/build.gradle +++ b/languages/kotlin/sdk/build.gradle @@ -2,7 +2,6 @@ plugins { id 'com.android.library' id 'org.jetbrains.kotlin.android' id 'maven-publish' - id 'com.palantir.git-version' version '3.0.0' } android { @@ -42,14 +41,25 @@ publishing { publications { maven(MavenPublication) { groupId = 'com.bitwarden' - artifactId = 'sdk' + artifactId = 'sdk-android' - def details = versionDetails() + // Determine the version from the git history. + // + // PRs: use the branch name. + // Master: Grab it from `crates/bitwarden/Cargo.toml` - if (details.branchName == null) { - version = "master-SNAPSHOT" + def branchName = "git branch --show-current".execute().text.trim() + + if (branchName == "master") { + def content = ['grep', '-o', '^version = ".*"', '../../crates/bitwarden/Cargo.toml'].execute().text.trim() + def match = ~/version = "(.*)"/ + def matcher = match.matcher(content) + matcher.find() + + version = "${matcher.group(1)}-SNAPSHOT" } else { - version "${details.branchName.replaceAll('/', '-')}-SNAPSHOT" + // branchName-SNAPSHOT + version = "${branchName.replaceAll('/', '-')}-SNAPSHOT" } afterEvaluate { @@ -60,7 +70,7 @@ publishing { repositories { maven { name = "GitHubPackages" - url = "https://maven.pkg.github.com/bitwarden/sdk-maven" + url = "https://maven.pkg.github.com/bitwarden/sdk" credentials { username = System.getenv("GITHUB_ACTOR") password = System.getenv("GITHUB_TOKEN") diff --git a/languages/swift/Package.swift b/languages/swift/Package.swift index 40f8f0afd..817bdce11 100644 --- a/languages/swift/Package.swift +++ b/languages/swift/Package.swift @@ -23,7 +23,8 @@ let package = Package( // Targets can depend on other targets in this package, and on products in packages this package depends on. .target( name: "BitwardenSdk", - dependencies: ["BitwardenFFI"]), + dependencies: ["BitwardenFFI"], + swiftSettings: [.unsafeFlags(["-suppress-warnings"])]), .testTarget( name: "BitwardenSdkTests", dependencies: ["BitwardenSdk"]), diff --git a/languages/swift/build.sh b/languages/swift/build.sh index 655a49ebf..69da98415 100755 --- a/languages/swift/build.sh +++ b/languages/swift/build.sh @@ -8,14 +8,16 @@ mkdir tmp mkdir -p tmp/target/universal-ios-sim/release # Build native library +export IPHONEOS_DEPLOYMENT_TARGET="13.0" +export RUSTFLAGS="-C link-arg=-Wl,-application_extension" cargo build --package bitwarden-uniffi --target aarch64-apple-ios-sim --release cargo build --package bitwarden-uniffi --target aarch64-apple-ios --release cargo build --package bitwarden-uniffi --target x86_64-apple-ios --release # Create universal libraries -lipo -create ../../target/aarch64-apple-ios-sim/release/libbitwarden_uniffi.dylib \ - ../../target/x86_64-apple-ios/release/libbitwarden_uniffi.dylib \ - -output ./tmp/target/universal-ios-sim/release/libbitwarden_uniffi.dylib +lipo -create ../../target/aarch64-apple-ios-sim/release/libbitwarden_uniffi.a \ + ../../target/x86_64-apple-ios/release/libbitwarden_uniffi.a \ + -output ./tmp/target/universal-ios-sim/release/libbitwarden_uniffi.a # Generate swift bindings cargo run -p uniffi-bindgen generate \ @@ -37,9 +39,9 @@ cat ./tmp/bindings/BitwardenFFI.modulemap ./tmp/bindings/BitwardenCoreFFI.module # Build xcframework xcodebuild -create-xcframework \ - -library ../../target/aarch64-apple-ios/release/libbitwarden_uniffi.dylib \ + -library ../../target/aarch64-apple-ios/release/libbitwarden_uniffi.a \ -headers ./tmp/Headers \ - -library ./tmp/target/universal-ios-sim/release/libbitwarden_uniffi.dylib \ + -library ./tmp/target/universal-ios-sim/release/libbitwarden_uniffi.a \ -headers ./tmp/Headers \ -output ./BitwardenFFI.xcframework diff --git a/package-lock.json b/package-lock.json index d3e3682db..22f0e6282 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,19 +9,19 @@ "version": "0.0.0", "license": "SEE LICENSE IN LICENSE", "devDependencies": { - "@openapitools/openapi-generator-cli": "2.6.0", + "@openapitools/openapi-generator-cli": "2.7.0", "handlebars": "^4.7.8", - "prettier": "3.0.0", - "quicktype-core": "23.0.59", - "rimraf": "5.0.0", + "prettier": "3.0.3", + "quicktype-core": "23.0.76", + "rimraf": "5.0.5", "ts-node": "10.9.1", - "typescript": "5.0.4" + "typescript": "5.2.2" } }, "node_modules/@babel/runtime": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.10.tgz", - "integrity": "sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.15.tgz", + "integrity": "sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA==", "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" @@ -179,115 +179,20 @@ } }, "node_modules/@nestjs/axios": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-0.0.8.tgz", - "integrity": "sha512-oJyfR9/h9tVk776il0829xyj3b2e81yTu6HjPraxynwNtMNGqZBHHmAQL24yMB3tVbBM0RvG3eUXH8+pRCGwlg==", + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-0.1.0.tgz", + "integrity": "sha512-b2TT2X6BFbnNoeteiaxCIiHaFcSbVW+S5yygYqiIq5i6H77yIU3IVuLdpQkHq8/EqOWFwMopLN8jdkUT71Am9w==", "dev": true, "dependencies": { "axios": "0.27.2" }, "peerDependencies": { - "@nestjs/common": "^7.0.0 || ^8.0.0", + "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0", "reflect-metadata": "^0.1.12", "rxjs": "^6.0.0 || ^7.0.0" } }, "node_modules/@nestjs/common": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-8.4.7.tgz", - "integrity": "sha512-m/YsbcBal+gA5CFrDpqXqsSfylo+DIQrkFY3qhVIltsYRfu8ct8J9pqsTO6OPf3mvqdOpFGrV5sBjoyAzOBvsw==", - "dev": true, - "peer": true, - "dependencies": { - "axios": "0.27.2", - "iterare": "1.2.1", - "tslib": "2.4.0", - "uuid": "8.3.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nest" - }, - "peerDependencies": { - "cache-manager": "*", - "class-transformer": "*", - "class-validator": "*", - "reflect-metadata": "^0.1.12", - "rxjs": "^7.1.0" - }, - "peerDependenciesMeta": { - "cache-manager": { - "optional": true - }, - "class-transformer": { - "optional": true - }, - "class-validator": { - "optional": true - } - } - }, - "node_modules/@nestjs/common/node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", - "dev": true, - "peer": true - }, - "node_modules/@nuxtjs/opencollective": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz", - "integrity": "sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "consola": "^2.15.0", - "node-fetch": "^2.6.1" - }, - "bin": { - "opencollective": "bin/opencollective.js" - }, - "engines": { - "node": ">=8.0.0", - "npm": ">=5.0.0" - } - }, - "node_modules/@openapitools/openapi-generator-cli": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@openapitools/openapi-generator-cli/-/openapi-generator-cli-2.6.0.tgz", - "integrity": "sha512-M/aOpR7G+Y1nMf+ofuar8pGszajgfhs1aSPSijkcr2tHTxKAI3sA3YYcOGbszxaNRKFyvOcDq+KP9pcJvKoCHg==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "@nestjs/axios": "0.0.8", - "@nestjs/common": "9.3.11", - "@nestjs/core": "9.3.11", - "@nuxtjs/opencollective": "0.3.2", - "chalk": "4.1.2", - "commander": "8.3.0", - "compare-versions": "4.1.4", - "concurrently": "6.5.1", - "console.table": "0.10.0", - "fs-extra": "10.1.0", - "glob": "7.1.6", - "inquirer": "8.2.5", - "lodash": "4.17.21", - "reflect-metadata": "0.1.13", - "rxjs": "7.8.0", - "tslib": "2.0.3" - }, - "bin": { - "openapi-generator-cli": "main.js" - }, - "engines": { - "node": ">=10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/openapi_generator" - } - }, - "node_modules/@openapitools/openapi-generator-cli/node_modules/@nestjs/common": { "version": "9.3.11", "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-9.3.11.tgz", "integrity": "sha512-IFZ2G/5UKWC2Uo7tJ4SxGed2+aiA+sJyWeWsGTogKVDhq90oxVBToh+uCDeI31HNUpqYGoWmkletfty42zUd8A==", @@ -320,13 +225,13 @@ } } }, - "node_modules/@openapitools/openapi-generator-cli/node_modules/@nestjs/common/node_modules/tslib": { + "node_modules/@nestjs/common/node_modules/tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", "dev": true }, - "node_modules/@openapitools/openapi-generator-cli/node_modules/@nestjs/core": { + "node_modules/@nestjs/core": { "version": "9.3.11", "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-9.3.11.tgz", "integrity": "sha512-CI27a2JFd5rvvbgkalWqsiwQNhcP4EAG5BUK8usjp29wVp1kx30ghfBT8FLqIgmkRVo65A0IcEnWsxeXMntkxQ==", @@ -364,12 +269,65 @@ } } }, - "node_modules/@openapitools/openapi-generator-cli/node_modules/@nestjs/core/node_modules/tslib": { + "node_modules/@nestjs/core/node_modules/tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", "dev": true }, + "node_modules/@nuxtjs/opencollective": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz", + "integrity": "sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "consola": "^2.15.0", + "node-fetch": "^2.6.1" + }, + "bin": { + "opencollective": "bin/opencollective.js" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, + "node_modules/@openapitools/openapi-generator-cli": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@openapitools/openapi-generator-cli/-/openapi-generator-cli-2.7.0.tgz", + "integrity": "sha512-ieEpHTA/KsDz7ANw03lLPYyjdedDEXYEyYoGBRWdduqXWSX65CJtttjqa8ZaB1mNmIjMtchUHwAYQmTLVQ8HYg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@nestjs/axios": "0.1.0", + "@nestjs/common": "9.3.11", + "@nestjs/core": "9.3.11", + "@nuxtjs/opencollective": "0.3.2", + "chalk": "4.1.2", + "commander": "8.3.0", + "compare-versions": "4.1.4", + "concurrently": "6.5.1", + "console.table": "0.10.0", + "fs-extra": "10.1.0", + "glob": "7.1.6", + "inquirer": "8.2.5", + "lodash": "4.17.21", + "reflect-metadata": "0.1.13", + "rxjs": "7.8.0", + "tslib": "2.0.3" + }, + "bin": { + "openapi-generator-cli": "main.js" + }, + "engines": { + "node": ">=10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/openapi_generator" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -405,16 +363,16 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.5.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.1.tgz", - "integrity": "sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg==", + "version": "20.6.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.5.tgz", + "integrity": "sha512-2qGq5LAOTh9izcc0+F+dToFigBWiK1phKPt7rNhOqJSr35y8rlIBjDwGtFSgAI6MGIhjwOVNSQZVdJsZJ2uR1w==", "dev": true, "peer": true }, "node_modules/@types/urijs": { - "version": "1.19.19", - "resolved": "https://registry.npmjs.org/@types/urijs/-/urijs-1.19.19.tgz", - "integrity": "sha512-FDJNkyhmKLw7uEvTxx5tSXfPeQpO0iy73Ry+PmYZJvQy0QIWX8a7kJ4kLWRf+EbTPJEPDSgPXHaM7pzr5lmvCg==", + "version": "1.19.20", + "resolved": "https://registry.npmjs.org/@types/urijs/-/urijs-1.19.20.tgz", + "integrity": "sha512-77Mq/2BeHU894J364dUv9tSwxxyCLtcX228Pc8TwZpP5bvOoMns+gZoftp3LYl3FBH8vChpWbuagKGiMki2c1A==", "dev": true }, "node_modules/abort-controller": { @@ -623,9 +581,9 @@ } }, "node_modules/cli-spinners": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.0.tgz", - "integrity": "sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.1.tgz", + "integrity": "sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==", "dev": true, "engines": { "node": ">=6" @@ -800,9 +758,9 @@ "dev": true }, "node_modules/cross-fetch": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", - "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", "dev": true, "dependencies": { "node-fetch": "^2.6.12" @@ -961,9 +919,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", "dev": true, "funding": [ { @@ -1233,9 +1191,9 @@ } }, "node_modules/jackspeak": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.0.tgz", - "integrity": "sha512-uKmsITSsF4rUWQHzqaRUuyAir3fZfW3f202Ee34lz/gZCi970CPZwyQXLGNgWJvvZbvFyzeyGq0+4fcG/mBKZg==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -1378,9 +1336,9 @@ "dev": true }, "node_modules/node-fetch": { - "version": "2.6.13", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.13.tgz", - "integrity": "sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dev": true, "dependencies": { "whatwg-url": "^5.0.0" @@ -1509,9 +1467,9 @@ } }, "node_modules/prettier": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz", - "integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -1533,22 +1491,22 @@ } }, "node_modules/quicktype-core": { - "version": "23.0.59", - "resolved": "https://registry.npmjs.org/quicktype-core/-/quicktype-core-23.0.59.tgz", - "integrity": "sha512-D8DwNyJkDi3kcQ0kY3QYHx/REobwa1kJ+2udTo7it1ofqtjQVNncRsjKN0urml6hByqdDCOT7IiFgOB7i0M2Nw==", + "version": "23.0.76", + "resolved": "https://registry.npmjs.org/quicktype-core/-/quicktype-core-23.0.76.tgz", + "integrity": "sha512-QinZRNovSTQcFuhRKxeHb22eFmyucbG96EPaQDSbz9qvIPxUhs1BZviNc8HAkHWYFqTSET/xZcEoHpm1DeDbRg==", "dev": true, "dependencies": { "@glideapps/ts-necessities": "2.1.3", "@types/urijs": "^1.19.19", "browser-or-node": "^2.1.1", "collection-utils": "^1.0.1", - "cross-fetch": "^3.1.5", + "cross-fetch": "^4.0.0", "is-url": "^1.2.4", "js-base64": "^3.7.5", "lodash": "^4.17.21", "pako": "^1.0.6", "pluralize": "^8.0.0", - "readable-stream": "4.3.0", + "readable-stream": "4.4.2", "unicode-properties": "^1.4.1", "urijs": "^1.19.1", "wordwrap": "^1.0.0", @@ -1580,15 +1538,16 @@ } }, "node_modules/quicktype-core/node_modules/readable-stream": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.3.0.tgz", - "integrity": "sha512-MuEnA0lbSi7JS8XM+WNJlWZkHAAdm7gETHdFK//Q/mChGyj2akEFtdLZh32jSdkWGbRwCW9pn6g3LWDdDeZnBQ==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.2.tgz", + "integrity": "sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA==", "dev": true, "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", - "process": "^0.11.10" + "process": "^0.11.10", + "string_decoder": "^1.3.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1643,15 +1602,15 @@ } }, "node_modules/rimraf": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.0.tgz", - "integrity": "sha512-Jf9llaP+RvaEVS5nPShYFhtXIrb3LRKP281ib3So0KkeZKo2wIKyq0Re7TOSwanasA423PSr6CCIL4bP6T040g==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", + "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", "dev": true, "dependencies": { - "glob": "^10.0.0" + "glob": "^10.3.7" }, "bin": { - "rimraf": "dist/cjs/src/bin.js" + "rimraf": "dist/esm/bin.mjs" }, "engines": { "node": ">=14" @@ -1670,19 +1629,19 @@ } }, "node_modules/rimraf/node_modules/glob": { - "version": "10.3.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.3.tgz", - "integrity": "sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw==", + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", "dev": true, "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", + "jackspeak": "^2.3.5", "minimatch": "^9.0.1", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", "path-scurry": "^1.10.1" }, "bin": { - "glob": "dist/cjs/src/bin.js" + "glob": "dist/esm/bin.mjs" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -1974,16 +1933,16 @@ } }, "node_modules/typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=12.20" + "node": ">=14.17" } }, "node_modules/uglify-js": { @@ -2058,16 +2017,6 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "peer": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -2171,9 +2120,9 @@ } }, "node_modules/yaml": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", - "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz", + "integrity": "sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==", "dev": true, "engines": { "node": ">= 14" diff --git a/package.json b/package.json index ef66a36bb..fdeb8fe58 100644 --- a/package.json +++ b/package.json @@ -20,12 +20,12 @@ "test": "echo \"Error: no test specified\" && exit 1" }, "devDependencies": { - "@openapitools/openapi-generator-cli": "2.6.0", + "@openapitools/openapi-generator-cli": "2.7.0", "handlebars": "^4.7.8", - "prettier": "3.0.0", - "quicktype-core": "23.0.59", - "rimraf": "5.0.0", + "prettier": "3.0.3", + "quicktype-core": "23.0.76", + "rimraf": "5.0.5", "ts-node": "10.9.1", - "typescript": "5.0.4" + "typescript": "5.2.2" } } diff --git a/support/docs/docs.ts b/support/docs/docs.ts index 331047fb9..14603dd57 100644 --- a/support/docs/docs.ts +++ b/support/docs/docs.ts @@ -24,9 +24,11 @@ const rootElements = [ "ClientCiphers", "ClientCollections", "ClientCrypto", + "ClientExporters", "ClientFolders", "ClientGenerators", "ClientPasswordHistory", + "ClientSends", "ClientVault", ]; diff --git a/support/scripts/schemas.ts b/support/scripts/schemas.ts index f5d5bce4e..148b089e6 100644 --- a/support/scripts/schemas.ts +++ b/support/scripts/schemas.ts @@ -56,12 +56,13 @@ async function main() { inputData, lang: "csharp", rendererOptions: { - namespace: "Bit.Sdk", + namespace: "Bitwarden.Sdk", + framework: "SystemTextJson", "csharp-version": "6", }, }); - writeToFile("./languages/csharp/schemas.cs", csharp.lines); + writeToFile("./languages/csharp/Bitwarden.Sdk/schemas.cs", csharp.lines); } main();