├── .cargo └── config.toml ├── .editorconfig ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── cli.yml │ ├── config.yml │ └── sdk.yml ├── PULL_REQUEST_TEMPLATE.md ├── codecov.yml ├── renovate.json ├── secrets │ └── devid-app-cert.p12.gpg └── workflows │ ├── build-cli-docker.yml │ ├── build-cli.yml │ ├── build-cpp.yml │ ├── build-dotnet.yml │ ├── build-go.yaml │ ├── build-java.yml │ ├── build-napi.yml │ ├── build-python-wheels.yml │ ├── build-ruby.yml │ ├── build-rust-crates.yml │ ├── build-rust-cross-platform.yml │ ├── build-wasm.yml │ ├── cloc.yml │ ├── direct-minimal-versions.yml │ ├── enforce-labels.yml │ ├── generate_schemas.yml │ ├── lint.yml │ ├── minimum-rust-version.yml │ ├── publish-bws.yml │ ├── publish-dotnet.yml │ ├── publish-java.yml │ ├── publish-napi.yml │ ├── publish-php.yml │ ├── publish-python.yml │ ├── publish-ruby.yml │ ├── publish-rust-crates.yml │ ├── publish-wasm.yml │ ├── release-bws.yml │ ├── release-cpp.yml │ ├── release-dotnet.yml │ ├── release-go.yml │ ├── release-java.yml │ ├── release-napi.yml │ ├── release-python.yml │ ├── release-ruby.yml │ ├── release-rust-crates.yml │ ├── release-wasm.yml │ ├── rust-test.yml │ ├── rustdoc.yml │ ├── scan.yml │ └── version-bump.yml ├── .gitignore ├── .prettierignore ├── .prettierrc.json ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── SECURITY.md ├── about.hbs ├── about.toml ├── bacon.toml ├── clippy.toml ├── crates ├── bitwarden-c │ ├── Cargo.toml │ └── src │ │ ├── c.rs │ │ ├── lib.rs │ │ └── macros │ │ ├── ffi.rs │ │ └── mod.rs ├── bitwarden-json │ ├── Cargo.toml │ └── src │ │ ├── client.rs │ │ ├── command.rs │ │ ├── lib.rs │ │ └── response.rs ├── bitwarden-napi │ ├── .npmignore │ ├── Cargo.toml │ ├── README.md │ ├── binding.d.ts │ ├── binding.js │ ├── build.rs │ ├── npm │ │ ├── darwin-arm64 │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ └── package.json │ │ ├── darwin-x64 │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ └── package.json │ │ ├── linux-x64-gnu │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ └── package.json │ │ └── win32-x64-msvc │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ └── package.json │ ├── package-lock.json │ ├── package.json │ ├── src-ts │ │ ├── bitwarden_client │ │ │ └── index.ts │ │ └── index.ts │ ├── src │ │ ├── client.rs │ │ └── lib.rs │ └── tsconfig.json ├── bitwarden-py │ ├── Cargo.toml │ ├── MANIFEST.in │ ├── build.rs │ ├── pyproject.toml │ └── src │ │ ├── client.rs │ │ ├── lib.rs │ │ └── python_module.rs ├── bitwarden-wasm │ ├── Cargo.toml │ ├── README.md │ ├── build.sh │ └── src │ │ ├── client.rs │ │ └── lib.rs ├── bitwarden │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── error.rs │ │ └── lib.rs ├── bws │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── Dockerfile │ ├── Dockerfile.dockerignore │ ├── README.md │ ├── build.rs │ ├── entitlements.plist │ ├── scripts │ │ ├── install.ps1 │ │ └── install.sh │ └── src │ │ ├── cli.rs │ │ ├── command │ │ ├── mod.rs │ │ ├── project.rs │ │ ├── run.rs │ │ └── secret.rs │ │ ├── config.rs │ │ ├── main.rs │ │ ├── render.rs │ │ ├── state.rs │ │ └── util.rs └── sdk-schemas │ ├── Cargo.toml │ └── src │ └── main.rs ├── languages ├── cpp │ ├── CMakeBuild.md │ ├── CMakeLists.txt │ ├── LICENSE.txt │ ├── README.md │ ├── examples │ │ ├── ExampleUse.md │ │ └── Wrapper.cpp │ ├── include │ │ ├── BitwardenClient.h │ │ ├── BitwardenLibrary.h │ │ ├── BitwardenSettings.h │ │ ├── CommandRunner.h │ │ ├── Projects.h │ │ └── Secrets.h │ ├── src │ │ ├── BitwardenClient.cpp │ │ ├── BitwardenLibrary.cpp │ │ ├── CommandRunner.cpp │ │ ├── Projects.cpp │ │ └── Secrets.cpp │ └── vcpkg.json ├── csharp │ ├── .editorconfig │ ├── Bitwarden.Sdk.Samples │ │ ├── Bitwarden.Sdk.Samples.csproj │ │ └── Program.cs │ ├── Bitwarden.Sdk.Tests │ │ ├── Bitwarden.Sdk.Tests.csproj │ │ ├── GlobalUsings.cs │ │ ├── InteropTests.cs │ │ ├── SampleTests.cs │ │ └── SecretsManagerFact.cs │ ├── Bitwarden.Sdk │ │ ├── AuthClient.cs │ │ ├── Bitwarden.Sdk.csproj │ │ ├── BitwardenAuthException.cs │ │ ├── BitwardenClient.Debug.cs │ │ ├── BitwardenClient.cs │ │ ├── BitwardenException.cs │ │ ├── BitwardenLibrary.cs │ │ ├── BitwardenSafeHandle.cs │ │ ├── BitwardenSettings.cs │ │ ├── CommandRunner.cs │ │ ├── ProjectsClient.cs │ │ ├── SecretsClient.cs │ │ └── bitwarden.png │ ├── Bitwarden.sln │ ├── LICENSE.txt │ ├── README.md │ └── global.json ├── go │ ├── .version │ ├── INSTRUCTIONS.md │ ├── LICENSE.txt │ ├── README.md │ ├── bitwarden_client.go │ ├── command_runner.go │ ├── example │ │ ├── example.go │ │ ├── go.mod │ │ └── go.sum │ ├── generators.go │ ├── go.mod │ ├── go.sum │ ├── internal │ │ └── cinterface │ │ │ └── bitwarden_library.go │ ├── project.go │ ├── secrets.go │ └── util.go ├── java │ ├── .gitignore │ ├── INSTALL.md │ ├── LICENSE.txt │ ├── README.md │ ├── build.gradle │ ├── example │ │ ├── Example.java │ │ └── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── bitwarden │ │ └── sdk │ │ ├── AuthClient.java │ │ ├── BitwardenClient.java │ │ ├── BitwardenClientException.java │ │ ├── BitwardenLibrary.java │ │ ├── BitwardenSettings.java │ │ ├── CommandRunner.java │ │ ├── ProjectsClient.java │ │ ├── SecretsClient.java │ │ └── ThrowingFunction.java ├── js │ ├── example │ │ ├── index.js │ │ ├── package-lock.json │ │ └── package.json │ ├── sdk-client │ │ ├── .gitignore │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── src │ │ │ ├── client.ts │ │ │ └── lib.ts │ │ └── tsconfig.json │ └── wasm │ │ ├── .gitignore │ │ ├── index.js │ │ └── package.json ├── php │ ├── .gitignore │ ├── INSTALL.md │ ├── LICENSE.txt │ ├── README.md │ ├── composer.json │ ├── composer.lock │ ├── example.php │ └── src │ │ ├── AuthClient.php │ │ ├── BitwardenClient.php │ │ ├── BitwardenLib.php │ │ ├── BitwardenSettings.php │ │ ├── CommandRunner.php │ │ ├── ProjectsClient.php │ │ └── SecretsClient.php ├── python │ ├── .gitignore │ ├── LICENSE.txt │ ├── README.md │ ├── bitwarden_sdk │ │ ├── __init__.py │ │ └── bitwarden_client.py │ ├── example.py │ ├── openapitools.json │ └── pyproject.toml └── ruby │ ├── .gitignore │ ├── CHANGELOG.md │ ├── LICENSE.txt │ ├── README.md │ ├── bitwarden_sdk_secrets │ ├── Gemfile │ ├── Rakefile │ ├── bitwarden-sdk-secrets.gemspec │ ├── lib │ │ ├── auth.rb │ │ ├── bitwarden-sdk-secrets.rb │ │ ├── bitwarden_error.rb │ │ ├── bitwarden_lib.rb │ │ ├── command_runner.rb │ │ ├── extended_schemas │ │ │ └── schemas.rb │ │ ├── projects.rb │ │ ├── secrets.rb │ │ └── version.rb │ ├── sig │ │ ├── auth.rbs │ │ ├── bitwarden-sdk-secrets.rbs │ │ ├── bitwarden_error.rbs │ │ ├── bitwarden_lib.rbs │ │ ├── command_runner.rbs │ │ ├── projects.rbs │ │ ├── secrets.rbs │ │ └── version.rbs │ └── spec │ │ └── settings_spec.rb │ ├── examples │ └── example.rb │ └── gen_ruby_typedefs.sh ├── package-lock.json ├── package.json ├── rustfmt.toml ├── sig └── bitwarden_sdk │ └── bitwarden_client.rbs └── support └── scripts └── schemas.ts /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(target_arch="aarch64")'] 2 | rustflags = ["--cfg", "aes_armv8"] 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.{js,ts,scss,html}] 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.ts] 16 | quote_type = double 17 | 18 | [*.rs] 19 | indent_style = space 20 | indent_size = 4 21 | 22 | [*.{kt,java,xml,gradle}] 23 | indent_style = space 24 | indent_size = 4 25 | 26 | [*.xml] 27 | # VS Code XML extension removes the final newline 28 | insert_final_newline = false 29 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Please sort into logical groups with comment headers. Sort groups in order of specificity. 2 | # For example, default owners should always be the first group. 3 | # Sort lines alphabetically within these groups to avoid accidentally adding duplicates. 4 | # 5 | # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners 6 | 7 | # BRE for publish workflow changes 8 | .github/workflows/publish-*.yml @bitwarden/dept-bre 9 | 10 | # Shared workflows ownership 11 | 12 | ## BRE & Platform teams shared ownership 13 | .github/workflows/release-* @bitwarden/dept-bre 14 | 15 | # BRE Automations 16 | crates/bws/Cargo.toml 17 | crates/bws/scripts/install.ps1 18 | crates/bws/scripts/install.sh 19 | 20 | ## Docker files have shared ownership ## 21 | **/Dockerfile 22 | **/*.Dockerfile 23 | **/.dockerignore 24 | **/entrypoint.sh 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/cli.yml: -------------------------------------------------------------------------------- 1 | name: Secrets Manager CLI Bug Report 2 | description: File a bug report for issues encountered using the Bitwarden Secrets Manager CLI (bws) 3 | labels: [bug, bws] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thanks for taking the time to fill out this bug report! 9 | 10 | Please do not submit feature requests. The [Community Forums](https://community.bitwarden.com) has a section for submitting, voting for, and discussing product feature requests. 11 | - type: textarea 12 | id: reproduce 13 | attributes: 14 | label: Steps To Reproduce 15 | description: How can we reproduce the behavior. 16 | value: | 17 | 1. Go to '...' 18 | 2. Click on '....' 19 | 3. Scroll down to '....' 20 | 4. Click on '...' 21 | validations: 22 | required: true 23 | - type: textarea 24 | id: expected 25 | attributes: 26 | label: Expected Result 27 | description: A clear and concise description of what you expected to happen. 28 | validations: 29 | required: true 30 | - type: textarea 31 | id: actual 32 | attributes: 33 | label: Actual Result 34 | description: A clear and concise description of what is happening. 35 | validations: 36 | required: true 37 | - type: textarea 38 | id: screenshots 39 | attributes: 40 | label: Screenshots or Videos 41 | description: If applicable, add screenshots and/or a short video to help explain your problem. 42 | - type: textarea 43 | id: additional-context 44 | attributes: 45 | label: Additional Context 46 | description: Add any other context about the problem here. 47 | - type: dropdown 48 | id: os 49 | attributes: 50 | label: Operating System 51 | description: What operating system are you seeing the problem on? 52 | multiple: true 53 | options: 54 | - Windows 55 | - macOS 56 | - Linux 57 | validations: 58 | required: true 59 | - type: input 60 | id: os-version 61 | attributes: 62 | label: Operating System Version 63 | description: What version of the operating system(s) are you seeing the problem on? 64 | - type: dropdown 65 | id: shell 66 | attributes: 67 | label: Shell 68 | description: What shell(s) are you seeing the problem on? 69 | multiple: true 70 | options: 71 | - Bash 72 | - Zsh 73 | - PowerShell 74 | validations: 75 | required: true 76 | - type: input 77 | id: version 78 | attributes: 79 | label: Build Version 80 | description: What version of our software are you running? (run `bws --version`) 81 | validations: 82 | required: true 83 | - type: checkboxes 84 | id: issue-tracking-info 85 | attributes: 86 | label: Issue Tracking Info 87 | description: | 88 | Issue tracking information 89 | options: 90 | - label: I understand that work is tracked outside of Github. A PR will be linked to this issue should one be opened to address it, but Bitwarden doesn't use fields like "assigned", "milestone", or "project" to track progress. 91 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Feature Requests 4 | url: https://community.bitwarden.com/c/feature-requests/ 5 | about: Request new features using the Community Forums. Please search existing feature requests before making a new one. 6 | - name: Bitwarden Community Forums 7 | url: https://community.bitwarden.com 8 | about: Please visit the community forums for general community discussion, support and the development roadmap. 9 | - name: Customer Support 10 | url: https://bitwarden.com/contact/ 11 | about: Please contact our customer support for account issues and general customer support. 12 | - name: Security Issues 13 | url: https://hackerone.com/bitwarden 14 | about: We use HackerOne to manage security disclosures. 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/sdk.yml: -------------------------------------------------------------------------------- 1 | name: SDK Bug Report 2 | description: File a bug report for issues encountered using the Bitwarden SDK 3 | labels: [bug, sdk] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thanks for taking the time to fill out this bug report! 9 | 10 | Please do not submit feature requests. The [Community Forums](https://community.bitwarden.com) has a section for submitting, voting for, and discussing product feature requests. 11 | - type: textarea 12 | id: reproduce 13 | attributes: 14 | label: Steps To Reproduce 15 | description: How can we reproduce the behavior. 16 | value: | 17 | 1. Go to '...' 18 | 2. Click on '....' 19 | 3. Scroll down to '....' 20 | 4. Click on '...' 21 | validations: 22 | required: true 23 | - type: textarea 24 | id: expected 25 | attributes: 26 | label: Expected Result 27 | description: A clear and concise description of what you expected to happen. 28 | validations: 29 | required: true 30 | - type: textarea 31 | id: actual 32 | attributes: 33 | label: Actual Result 34 | description: A clear and concise description of what is happening. 35 | validations: 36 | required: true 37 | - type: textarea 38 | id: screenshots 39 | attributes: 40 | label: Screenshots or Videos 41 | description: If applicable, add screenshots and/or a short video to help explain your problem. 42 | - type: textarea 43 | id: additional-context 44 | attributes: 45 | label: Additional Context 46 | description: Add any other context about the problem here. 47 | - type: dropdown 48 | id: os 49 | attributes: 50 | label: Operating System 51 | description: What operating system are you seeing the problem on? 52 | multiple: true 53 | options: 54 | - Windows 55 | - macOS 56 | - Linux 57 | validations: 58 | required: true 59 | - type: input 60 | id: os-version 61 | attributes: 62 | label: Operating System Version 63 | description: What version of the operating system(s) are you seeing the problem on? 64 | - type: input 65 | id: version 66 | attributes: 67 | label: Build Version 68 | description: What version of the SDK are you running? 69 | validations: 70 | required: true 71 | - type: checkboxes 72 | id: issue-tracking-info 73 | attributes: 74 | label: Issue Tracking Info 75 | description: | 76 | Issue tracking information 77 | options: 78 | - label: I understand that work is tracked outside of Github. A PR will be linked to this issue should one be opened to address it, but Bitwarden doesn't use fields like "assigned", "milestone", or "project" to track progress. 79 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## 🎟️ Tracking 2 | 3 | 4 | 5 | ## 📔 Objective 6 | 7 | 8 | 9 | ## ⏰ Reminders before review 10 | 11 | - Contributor guidelines followed 12 | - All formatters and local linters executed and passed 13 | - Written new unit and / or integration tests where applicable 14 | - Protected functional changes with optionality (feature flags) 15 | - Used internationalization (i18n) for all UI strings 16 | - CI builds passed 17 | - Communicated to DevOps any deployment requirements 18 | - Updated any necessary documentation (Confluence, contributing docs) or informed the documentation 19 | team 20 | 21 | ## 🦮 Reviewer guidelines 22 | 23 | 24 | 25 | - 👍 (`:+1:`) or similar for great changes 26 | - 📝 (`:memo:`) or ℹ️ (`:information_source:`) for notes or general info 27 | - ❓ (`:question:`) for questions 28 | - 🤔 (`:thinking:`) or 💭 (`:thought_balloon:`) for more open inquiry that's not quite a confirmed 29 | issue and could potentially benefit from discussion 30 | - 🎨 (`:art:`) for suggestions / improvements 31 | - ❌ (`:x:`) or ⚠️ (`:warning:`) for more significant problems or concerns needing attention 32 | - 🌱 (`:seedling:`) or ♻️ (`:recycle:`) for future improvements or indications of technical debt 33 | - ⛏ (`:pick:`) for minor or nitpick changes 34 | -------------------------------------------------------------------------------- /.github/codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "crates/sdk-schemas" # Tool 3 | - "crates/uniffi-bindgen" # Tool 4 | - "crates/memory-testing" # Testing 5 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["github>bitwarden/renovate-config:non-pinned"], 4 | "separateMajorMinor": true, 5 | "enabledManagers": ["cargo", "dockerfile", "github-actions", "gomod", "npm", "nuget"], 6 | "constraints": { 7 | "go": "1.21" 8 | }, 9 | "packageRules": [ 10 | { 11 | "matchManagers": ["cargo"], 12 | "matchPackagePatterns": ["pyo3*"], 13 | "matchUpdateTypes": ["minor", "patch"], 14 | "groupName": "pyo3 non-major" 15 | }, 16 | { 17 | "groupName": "dockerfile minor", 18 | "matchManagers": ["dockerfile"], 19 | "matchUpdateTypes": ["minor", "patch"] 20 | }, 21 | { 22 | "groupName": "gh minor", 23 | "matchManagers": ["github-actions"], 24 | "matchUpdateTypes": ["minor", "patch"] 25 | }, 26 | { 27 | "groupName": "go minor", 28 | "matchManagers": ["gomod"], 29 | "matchUpdateTypes": ["minor", "patch"] 30 | } 31 | ], 32 | "ignoreDeps": ["dotnet-sdk"] 33 | } 34 | -------------------------------------------------------------------------------- /.github/secrets/devid-app-cert.p12.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitwarden/sdk-sm/e9a98cddda1cdf417b9c1d4872b3ed6bcb2beec9/.github/secrets/devid-app-cert.p12.gpg -------------------------------------------------------------------------------- /.github/workflows/build-go.yaml: -------------------------------------------------------------------------------- 1 | name: Build Go SDK 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - rc 8 | - hotfix-rc 9 | 10 | pull_request: 11 | 12 | env: 13 | GO111MODULE: on 14 | GO_VERSION: "^1.21" 15 | 16 | jobs: 17 | build: 18 | name: Build 19 | runs-on: ubuntu-22.04 20 | steps: 21 | - name: Checkout Repository 22 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 23 | 24 | - name: Setup Go environment 25 | uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0 26 | with: 27 | go-version: ${{ env.GO_VERSION }} 28 | 29 | - name: Cache dependencies 30 | uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 31 | with: 32 | path: ~/go/pkg/mod 33 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 34 | restore-keys: | 35 | ${{ runner.os }}-go- 36 | 37 | - name: Run npm ci 38 | run: npm ci 39 | 40 | - name: Generate schemas 41 | run: npm run schemas 42 | 43 | - name: Build 44 | working-directory: languages/go 45 | run: go build -v ./... 46 | 47 | - name: Test 48 | working-directory: languages/go 49 | run: go test -v ./... 50 | -------------------------------------------------------------------------------- /.github/workflows/build-napi.yml: -------------------------------------------------------------------------------- 1 | name: Build @bitwarden/sdk-napi 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - "main" 8 | - "rc" 9 | - "hotfix-rc" 10 | workflow_dispatch: 11 | 12 | defaults: 13 | run: 14 | shell: bash 15 | working-directory: crates/bitwarden-napi 16 | 17 | jobs: 18 | generate_schemas: 19 | name: Generate schemas 20 | uses: ./.github/workflows/generate_schemas.yml 21 | 22 | build: 23 | name: Building @bitwarden/sdk-napi for - ${{ matrix.settings.os }} 24 | runs-on: ${{ matrix.settings.os || 'ubuntu-24.04' }} 25 | needs: generate_schemas 26 | strategy: 27 | fail-fast: false 28 | matrix: 29 | settings: 30 | - os: macos-13 31 | target: x86_64-apple-darwin 32 | build: | 33 | npm run build 34 | strip -x *.node 35 | 36 | - os: macos-13 37 | target: aarch64-apple-darwin 38 | build: | 39 | npm run build-arm64 40 | strip -x *.node 41 | 42 | - os: windows-2022 43 | target: x86_64-pc-windows-msvc 44 | build: npm run build 45 | 46 | - os: ubuntu-22.04 47 | target: x86_64-unknown-linux-gnu 48 | build: | 49 | set -e && 50 | npm run build && 51 | strip *.node 52 | steps: 53 | - name: Checkout repo 54 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 55 | 56 | - name: Setup Node 57 | uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 58 | with: 59 | node-version: 18 60 | cache: "npm" 61 | cache-dependency-path: crates/bitwarden-napi/package-lock.json 62 | 63 | - name: Install rust 64 | uses: dtolnay/rust-toolchain@c5a29ddb4d9d194e7c84ec8c3fba61b1c31fee8c # stable 65 | with: 66 | toolchain: stable 67 | targets: ${{ matrix.settings.target }} 68 | 69 | - name: Cache cargo registry 70 | uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5 71 | with: 72 | key: ${{ matrix.settings.target }}-cargo-${{ matrix.settings.os }} 73 | 74 | - name: Retrieve schemas 75 | uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 76 | with: 77 | name: schemas.ts 78 | path: ${{ github.workspace }}/crates/bitwarden-napi/src-ts/bitwarden_client/ 79 | 80 | - name: Install dependencies 81 | run: npm ci 82 | 83 | - name: Build 84 | run: ${{ matrix.settings.build }} 85 | 86 | - name: Upload artifact 87 | uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 88 | with: 89 | name: sdk-bitwarden-napi-${{ matrix.settings.target }} 90 | path: ${{ github.workspace }}/crates/bitwarden-napi/sdk-napi.*.node 91 | if-no-files-found: error 92 | -------------------------------------------------------------------------------- /.github/workflows/build-rust-crates.yml: -------------------------------------------------------------------------------- 1 | name: Build Rust crates 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | push: 7 | branches: 8 | - "main" 9 | 10 | env: 11 | CARGO_TERM_COLOR: always 12 | 13 | jobs: 14 | build: 15 | name: Building ${{matrix.package}} for - ${{ matrix.os }} 16 | 17 | runs-on: ${{ matrix.os || 'ubuntu-24.04' }} 18 | 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | os: 23 | - macos-13 24 | - ubuntu-24.04 25 | - windows-2022 26 | 27 | package: 28 | - bitwarden 29 | 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 33 | 34 | - name: Install rust 35 | uses: dtolnay/rust-toolchain@c5a29ddb4d9d194e7c84ec8c3fba61b1c31fee8c # stable 36 | with: 37 | toolchain: stable 38 | 39 | - name: Cache cargo registry 40 | uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5 41 | 42 | - name: Build 43 | run: cargo build -p ${{ matrix.package }} --release 44 | env: 45 | RUSTFLAGS: "-D warnings" 46 | 47 | release-dry-run: 48 | name: Release dry-run 49 | runs-on: ubuntu-24.04 50 | if: ${{ github.ref == 'refs/head/main' }} 51 | needs: build 52 | steps: 53 | - name: Checkout 54 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 55 | 56 | - name: Install rust 57 | uses: dtolnay/rust-toolchain@c5a29ddb4d9d194e7c84ec8c3fba61b1c31fee8c # stable 58 | with: 59 | toolchain: stable 60 | 61 | - name: Cache cargo registry 62 | uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5 63 | 64 | - name: Install cargo-release 65 | run: cargo install cargo-release 66 | 67 | - name: Cargo release dry run 68 | run: cargo-release release publish --no-publish -p bitwarden 69 | -------------------------------------------------------------------------------- /.github/workflows/build-wasm.yml: -------------------------------------------------------------------------------- 1 | name: Build @bitwarden/sdk-wasm 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - "main" 8 | - "rc" 9 | - "hotfix-rc" 10 | workflow_dispatch: 11 | 12 | defaults: 13 | run: 14 | shell: bash 15 | working-directory: crates/bitwarden-wasm 16 | 17 | jobs: 18 | build: 19 | name: Building @bitwarden/sdk-wasm 20 | runs-on: ubuntu-22.04 21 | 22 | steps: 23 | - name: Checkout repo 24 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 25 | 26 | - name: Setup Node 27 | uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 28 | with: 29 | node-version: 18 30 | registry-url: "https://npm.pkg.github.com" 31 | cache: "npm" 32 | 33 | - name: Install dependencies 34 | run: npm i -g binaryen 35 | 36 | - name: Install rust 37 | uses: dtolnay/rust-toolchain@c5a29ddb4d9d194e7c84ec8c3fba61b1c31fee8c # stable 38 | with: 39 | toolchain: 1.81.0 40 | targets: wasm32-unknown-unknown 41 | 42 | - name: Cache cargo registry 43 | uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5 44 | with: 45 | key: wasm-cargo-cache 46 | 47 | - name: Install wasm-bindgen-cli 48 | run: cargo install wasm-bindgen-cli --version 0.2.95 49 | 50 | - name: Build 51 | run: ./build.sh -r 52 | 53 | - name: Upload artifact 54 | uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 55 | with: 56 | name: sdk-bitwarden-wasm 57 | path: ${{ github.workspace }}/languages/js/wasm/* 58 | if-no-files-found: error 59 | 60 | - name: Set version 61 | if: ${{ github.ref == 'refs/heads/main' }} 62 | # Fetches current version from registry and uses prerelease to bump it 63 | run: | 64 | npm version --no-git-tag-version $(npm view @bitwarden/sdk-wasm@latest version) 65 | npm version --no-git-tag-version prerelease 66 | env: 67 | NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 68 | working-directory: languages/js/wasm 69 | 70 | - name: Publish NPM 71 | if: ${{ github.ref == 'refs/heads/main' }} 72 | run: npm publish --access public 73 | env: 74 | NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 75 | working-directory: languages/js/wasm 76 | -------------------------------------------------------------------------------- /.github/workflows/cloc.yml: -------------------------------------------------------------------------------- 1 | name: CLOC 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: ["main"] 7 | pull_request: 8 | 9 | jobs: 10 | cloc: 11 | name: CLOC 12 | runs-on: ubuntu-22.04 13 | steps: 14 | - name: Checkout repo 15 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 16 | 17 | - name: Set up cloc 18 | run: | 19 | sudo apt update 20 | sudo apt -y install cloc 21 | 22 | - name: Print lines of code 23 | run: cloc --vcs git 24 | -------------------------------------------------------------------------------- /.github/workflows/direct-minimal-versions.yml: -------------------------------------------------------------------------------- 1 | name: Direct Minimum Version 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - "main" 8 | - "rc" 9 | - "hotfix-rc" 10 | workflow_dispatch: 11 | 12 | defaults: 13 | run: 14 | shell: bash 15 | 16 | jobs: 17 | direct-minimal-versions: 18 | name: Check dependencies minimal versions for - ${{ matrix.settings.os }} - ${{ matrix.settings.target }} 19 | runs-on: ${{ matrix.settings.os || 'ubuntu-24.04' }} 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | settings: 24 | #- os: macos-13 25 | # target: x86_64-apple-darwin 26 | 27 | #- os: macos-13 28 | # target: aarch64-apple-darwin 29 | 30 | - os: windows-2022 31 | target: x86_64-pc-windows-msvc 32 | 33 | - os: ubuntu-22.04 34 | target: x86_64-unknown-linux-gnu 35 | 36 | steps: 37 | - name: Checkout 38 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 39 | 40 | - name: Install rust 41 | uses: dtolnay/rust-toolchain@c5a29ddb4d9d194e7c84ec8c3fba61b1c31fee8c # stable 42 | with: 43 | toolchain: nightly 44 | targets: ${{ matrix.settings.target }} 45 | 46 | - name: Cache cargo registry 47 | uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5 48 | with: 49 | key: dmv-${{ matrix.settings.target }}-cargo-${{ matrix.settings.os }} 50 | 51 | - name: Run cargo check direct-minimal-versions 52 | run: cargo check -Z direct-minimal-versions --all-features 53 | -------------------------------------------------------------------------------- /.github/workflows/enforce-labels.yml: -------------------------------------------------------------------------------- 1 | name: Enforce PR labels 2 | 3 | on: 4 | workflow_call: 5 | pull_request: 6 | types: [labeled, unlabeled, opened, edited, synchronize] 7 | jobs: 8 | enforce-label: 9 | name: EnforceLabel 10 | runs-on: ubuntu-22.04 11 | steps: 12 | - name: Enforce Label 13 | uses: yogevbd/enforce-label-action@a3c219da6b8fa73f6ba62b68ff09c469b3a1c024 # 2.2.2 14 | with: 15 | BANNED_LABELS: "hold,needs-qa" 16 | BANNED_LABELS_DESCRIPTION: "PRs with the hold or needs-qa labels cannot be merged" 17 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: ["main"] 7 | pull_request: 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | style: 14 | name: Check Style 15 | 16 | runs-on: ubuntu-24.04 17 | 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 21 | 22 | - name: Install rust 23 | uses: dtolnay/rust-toolchain@c5a29ddb4d9d194e7c84ec8c3fba61b1c31fee8c # stable 24 | with: 25 | toolchain: stable 26 | 27 | - name: Install rust nightly 28 | run: | 29 | rustup toolchain install nightly 30 | rustup component add rustfmt --toolchain nightly-x86_64-unknown-linux-gnu 31 | 32 | - name: Cache cargo registry 33 | uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5 34 | 35 | - name: Cargo fmt 36 | run: cargo +nightly fmt --check 37 | 38 | - name: Install clippy-sarif and sarif-fmt 39 | run: cargo install clippy-sarif sarif-fmt --locked --git https://github.com/psastras/sarif-rs.git --rev 11c33a53f6ffeaed736856b86fb6b7b09fabdfd8 40 | 41 | - name: Cargo clippy 42 | run: cargo clippy --all-features --tests --message-format=json | 43 | clippy-sarif | tee clippy_result.sarif | sarif-fmt 44 | env: 45 | RUSTFLAGS: "-D warnings" 46 | 47 | - name: Upload Clippy results to GitHub 48 | uses: github/codeql-action/upload-sarif@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0 49 | with: 50 | sarif_file: clippy_result.sarif 51 | sha: ${{ contains(github.event_name, 'pull_request') && github.event.pull_request.head.sha || github.sha }} 52 | ref: ${{ contains(github.event_name, 'pull_request') && format('refs/pull/{0}/head', github.event.pull_request.number) || github.ref }} 53 | 54 | - name: Set up Node 55 | uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 56 | with: 57 | cache: "npm" 58 | cache-dependency-path: "package-lock.json" 59 | node-version: "16" 60 | 61 | - name: NPM setup 62 | run: npm ci 63 | 64 | - name: Node Lint 65 | run: npm run lint 66 | 67 | - name: Verify rust documentation links 68 | run: cargo doc --no-deps 69 | env: 70 | RUSTDOCFLAGS: "-D warnings" 71 | -------------------------------------------------------------------------------- /.github/workflows/minimum-rust-version.yml: -------------------------------------------------------------------------------- 1 | name: Minimum Rust Version 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - "main" 8 | - "rc" 9 | - "hotfix-rc" 10 | workflow_dispatch: 11 | 12 | defaults: 13 | run: 14 | shell: bash 15 | 16 | jobs: 17 | msrv: 18 | name: Check MSRV for - ${{ matrix.settings.os }} - ${{ matrix.settings.target }} 19 | runs-on: ${{ matrix.settings.os || 'ubuntu-24.04' }} 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | settings: 24 | - os: ubuntu-22.04 25 | target: x86_64-unknown-linux-gnu 26 | 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 30 | 31 | - name: Install rust 32 | uses: dtolnay/rust-toolchain@c5a29ddb4d9d194e7c84ec8c3fba61b1c31fee8c # stable 33 | with: 34 | # Important: When updating this, make sure to update the Readme file 35 | # and also the `rust-version` field in all the `Cargo.toml`. 36 | toolchain: 1.75.0 37 | targets: ${{ matrix.settings.target }} 38 | 39 | - name: Cache cargo registry 40 | uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5 41 | with: 42 | key: msrv-${{ matrix.settings.target }}-cargo-${{ matrix.settings.os }} 43 | 44 | - name: Run cargo check MSRV 45 | run: cargo check -p bitwarden --all-features 46 | -------------------------------------------------------------------------------- /.github/workflows/release-bws.yml: -------------------------------------------------------------------------------- 1 | name: Release bws CLI 2 | run-name: Release bws CLI ${{ inputs.release_type }} 3 | 4 | on: 5 | workflow_dispatch: 6 | inputs: 7 | release_type: 8 | description: "Release Options" 9 | required: true 10 | default: "Release" 11 | type: choice 12 | options: 13 | - Release 14 | - Dry Run 15 | 16 | jobs: 17 | setup: 18 | name: Setup 19 | runs-on: ubuntu-22.04 20 | outputs: 21 | release_version: ${{ steps.version.outputs.version }} 22 | steps: 23 | - name: Checkout repo 24 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 25 | 26 | - name: Branch check 27 | if: ${{ inputs.release_type != 'Dry Run' }} 28 | run: | 29 | if [[ "$GITHUB_REF" != "refs/heads/main" ]]; then 30 | echo "===================================" 31 | echo "[!] Can only release from the 'main' branch" 32 | echo "===================================" 33 | exit 1 34 | fi 35 | 36 | - name: Check Release Version 37 | id: version 38 | run: | 39 | VERSION=$(grep -o '^version = ".*"' crates/bws/Cargo.toml | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+") 40 | echo "version=$VERSION" >> $GITHUB_OUTPUT 41 | 42 | - name: Download all Release artifacts 43 | uses: bitwarden/gh-actions/download-artifacts@main 44 | with: 45 | workflow: build-cli.yml 46 | path: packages 47 | workflow_conclusion: success 48 | branch: ${{ github.ref_name }} 49 | 50 | - name: Get checksum files 51 | uses: bitwarden/gh-actions/get-checksum@main 52 | with: 53 | packages_dir: "packages" 54 | file_path: "packages/bws-sha256-checksums-${{ steps.version.outputs.version }}.txt" 55 | 56 | - name: Create release 57 | if: ${{ inputs.release_type != 'Dry Run' }} 58 | uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0 59 | env: 60 | PKG_VERSION: ${{ steps.version.outputs.version }} 61 | with: 62 | artifacts: "packages/bws-x86_64-apple-darwin-${{ env.PKG_VERSION }}.zip, 63 | packages/bws-aarch64-apple-darwin-${{ env.PKG_VERSION }}.zip, 64 | packages/bws-macos-universal-${{ env.PKG_VERSION }}.zip, 65 | packages/bws-x86_64-pc-windows-msvc-${{ env.PKG_VERSION }}.zip, 66 | packages/bws-aarch64-pc-windows-msvc-${{ env.PKG_VERSION }}.zip, 67 | packages/bws-x86_64-unknown-linux-gnu-${{ env.PKG_VERSION }}.zip, 68 | packages/bws-aarch64-unknown-linux-gnu-${{ env.PKG_VERSION }}.zip, 69 | packages/THIRDPARTY.html, 70 | packages/bws-sha256-checksums-${{ env.PKG_VERSION }}.txt" 71 | commit: ${{ github.sha }} 72 | tag: bws-v${{ env.PKG_VERSION }} 73 | name: bws CLI v${{ env.PKG_VERSION }} 74 | body: "" 75 | token: ${{ secrets.GITHUB_TOKEN }} 76 | draft: true 77 | -------------------------------------------------------------------------------- /.github/workflows/release-dotnet.yml: -------------------------------------------------------------------------------- 1 | name: Release .NET NuGet 2 | run-name: Release .NET NuGet Package ${{ inputs.release_type }} 3 | 4 | on: 5 | workflow_dispatch: 6 | inputs: 7 | release_type: 8 | description: "Release Options" 9 | required: true 10 | default: "Release" 11 | type: choice 12 | options: 13 | - Release 14 | - Dry Run 15 | 16 | jobs: 17 | setup: 18 | name: Setup 19 | runs-on: ubuntu-22.04 20 | outputs: 21 | version: ${{ steps.version.outputs.version }} 22 | steps: 23 | - name: Checkout repo 24 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 25 | 26 | - name: Branch check 27 | if: ${{ inputs.release_type != 'Dry Run' }} 28 | run: | 29 | if [[ "$GITHUB_REF" != "refs/heads/main" ]]; then 30 | echo "===================================" 31 | echo "[!] Can only release from the 'main' branch" 32 | echo "===================================" 33 | exit 1 34 | fi 35 | 36 | - name: Install xmllint 37 | run: sudo apt-get install -y libxml2-utils 38 | 39 | - name: Get version 40 | id: version 41 | run: | 42 | VERSION=$(xmllint --xpath 'string(/Project/PropertyGroup/Version)' languages/csharp/Bitwarden.Sdk/Bitwarden.Sdk.csproj) 43 | echo "version=$VERSION" >> $GITHUB_OUTPUT 44 | 45 | release: 46 | name: Create GitHub release 47 | runs-on: ubuntu-22.04 48 | needs: setup 49 | steps: 50 | - name: Checkout Repository 51 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 52 | 53 | - name: Download NuGet package 54 | uses: bitwarden/gh-actions/download-artifacts@main 55 | with: 56 | workflow: build-dotnet.yml 57 | workflow_conclusion: success 58 | branch: main 59 | artifacts: Bitwarden.Sdk.${{ needs.setup.outputs.version }}.nupkg 60 | path: ./nuget-output 61 | 62 | - name: Create release 63 | if: ${{ inputs.release_type != 'Dry Run' }} 64 | uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0 65 | env: 66 | PKG_VERSION: ${{ needs.setup.outputs.version }} 67 | with: 68 | commit: ${{ github.sha }} 69 | tag: dotnet-v${{ env.PKG_VERSION }} 70 | name: .NET NuGet v${{ env.PKG_VERSION }} 71 | body: "" 72 | token: ${{ secrets.GITHUB_TOKEN }} 73 | draft: true 74 | artifacts: | 75 | ./nuget-output/Bitwarden.Secrets.Sdk.${{ needs.setup.outputs.version }}.nupkg 76 | -------------------------------------------------------------------------------- /.github/workflows/release-java.yml: -------------------------------------------------------------------------------- 1 | name: Release Java SDK 2 | run-name: Release Java SDK ${{ inputs.release_type }} 3 | 4 | on: 5 | workflow_dispatch: 6 | inputs: 7 | release_type: 8 | description: "Release Options" 9 | required: true 10 | default: "Release" 11 | type: choice 12 | options: 13 | - Release 14 | - Dry Run 15 | 16 | jobs: 17 | setup: 18 | name: Setup 19 | runs-on: ubuntu-22.04 20 | outputs: 21 | version: ${{ steps.version.outputs.version }} 22 | steps: 23 | - name: Checkout repo 24 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 25 | 26 | - name: Branch check 27 | if: ${{ inputs.release_type != 'Dry Run' }} 28 | run: | 29 | if [[ "$GITHUB_REF" != "refs/heads/main" ]]; then 30 | echo "===================================" 31 | echo "[!] Can only release from the 'main' branch" 32 | echo "===================================" 33 | exit 1 34 | fi 35 | 36 | - name: Get version 37 | id: version 38 | run: | 39 | VERSION=$(cat languages/java/build.gradle | grep -Eo 'version = "[0-9]+\.[0-9]+\.[0-9]+"' | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+') 40 | echo "version=$VERSION" >> $GITHUB_OUTPUT 41 | 42 | release: 43 | name: Release 44 | runs-on: ubuntu-22.04 45 | needs: setup 46 | steps: 47 | - name: Checkout Repository 48 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 49 | 50 | - name: Create release 51 | if: ${{ inputs.release_type != 'Dry Run' }} 52 | uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0 53 | env: 54 | PKG_VERSION: ${{ needs.setup.outputs.version }} 55 | with: 56 | commit: ${{ github.sha }} 57 | tag: java-v${{ env.PKG_VERSION }} 58 | name: Java SDK v${{ env.PKG_VERSION }} 59 | body: "" 60 | token: ${{ secrets.GITHUB_TOKEN }} 61 | draft: true 62 | -------------------------------------------------------------------------------- /.github/workflows/release-python.yml: -------------------------------------------------------------------------------- 1 | name: Release Python SDK 2 | run-name: Release Python SDK ${{ inputs.release_type }} 3 | 4 | on: 5 | workflow_dispatch: 6 | inputs: 7 | release_type: 8 | description: "Release Options" 9 | required: true 10 | default: "Release" 11 | type: choice 12 | options: 13 | - Release 14 | - Dry Run 15 | 16 | jobs: 17 | setup: 18 | name: Setup 19 | runs-on: ubuntu-22.04 20 | outputs: 21 | version: ${{ steps.version.outputs.version }} 22 | steps: 23 | - name: Checkout repo 24 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 25 | 26 | - name: Branch check 27 | if: ${{ inputs.release_type != 'Dry Run' }} 28 | run: | 29 | if [[ "$GITHUB_REF" != "refs/heads/main" ]]; then 30 | echo "===================================" 31 | echo "[!] Can only release from the 'main' branch" 32 | echo "===================================" 33 | exit 1 34 | fi 35 | 36 | - name: Get version 37 | id: version 38 | run: | 39 | VERSION=$(cat languages/python/pyproject.toml | grep -Eo 'version = "[0-9]+\.[0-9]+\.[0-9]+"' | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+') 40 | echo "version=$VERSION" >> $GITHUB_OUTPUT 41 | 42 | release: 43 | name: Release 44 | runs-on: ubuntu-22.04 45 | needs: setup 46 | steps: 47 | - name: Checkout repo 48 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 49 | 50 | - name: Download artifacts 51 | uses: dawidd6/action-download-artifact@bf251b5aa9c2f7eeb574a96ee720e24f801b7c11 # v6 52 | with: 53 | workflow: build-python-wheels.yml 54 | path: ${{ github.workspace }}/target/wheels/dist 55 | workflow_conclusion: success 56 | branch: main 57 | name: bitwarden_sdk(.*) 58 | name_is_regexp: true 59 | 60 | - name: Move all whl files to single directory 61 | run: | 62 | shopt -s globstar 63 | mv **/*.whl . 64 | working-directory: ${{ github.workspace }}/target/wheels/dist 65 | 66 | - name: Create GitHub release 67 | if: ${{ inputs.release_type != 'Dry Run' }} 68 | uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0 69 | env: 70 | PKG_VERSION: ${{ needs.setup.outputs.version }} 71 | with: 72 | commit: ${{ github.sha }} 73 | tag: python-v${{ env.PKG_VERSION }} 74 | name: Python v${{ env.PKG_VERSION }} 75 | body: "" 76 | token: ${{ secrets.GITHUB_TOKEN }} 77 | draft: true 78 | artifacts: | 79 | ${{ github.workspace }}/target/wheels/dist/bitwarden_sdk-*.whl 80 | -------------------------------------------------------------------------------- /.github/workflows/release-ruby.yml: -------------------------------------------------------------------------------- 1 | name: Release Ruby SDK 2 | run-name: Release Ruby SDK ${{ inputs.release_type }} 3 | 4 | on: 5 | workflow_dispatch: 6 | inputs: 7 | release_type: 8 | description: "Release Options" 9 | required: true 10 | default: "Release" 11 | type: choice 12 | options: 13 | - Release 14 | - Dry Run 15 | 16 | jobs: 17 | setup: 18 | name: Setup 19 | runs-on: ubuntu-22.04 20 | outputs: 21 | version: ${{ steps.version.outputs.version }} 22 | steps: 23 | - name: Checkout repo 24 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 25 | 26 | - name: Branch check 27 | if: ${{ inputs.release_type != 'Dry Run' }} 28 | run: | 29 | if [[ "$GITHUB_REF" != "refs/heads/main" ]]; then 30 | echo "===================================" 31 | echo "[!] Can only release from the 'main' branch" 32 | echo "===================================" 33 | exit 1 34 | fi 35 | 36 | - name: Get version 37 | id: version 38 | run: | 39 | VERSION=$(cat languages/ruby/lib/version.rb | grep -Eo 'VERSION = "[0-9]+\.[0-9]+\.[0-9]+"' | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+') 40 | echo "version=$VERSION" >> $GITHUB_OUTPUT 41 | 42 | release: 43 | name: Create GitHub release 44 | runs-on: ubuntu-22.04 45 | needs: setup 46 | steps: 47 | - name: Checkout Repository 48 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 49 | 50 | - name: Download ruby artifact 51 | uses: bitwarden/gh-actions/download-artifacts@main 52 | with: 53 | workflow: build-ruby.yml 54 | workflow_conclusion: success 55 | branch: main 56 | artifacts: bitwarden-sdk-secrets 57 | 58 | - name: Create release 59 | if: ${{ inputs.release_type != 'Dry Run' }} 60 | uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0 61 | env: 62 | PKG_VERSION: ${{ needs.setup.outputs.version }} 63 | with: 64 | commit: ${{ github.sha }} 65 | tag: ruby-v${{ env.PKG_VERSION }} 66 | name: Ruby v${{ env.PKG_VERSION }} 67 | body: "" 68 | token: ${{ secrets.GITHUB_TOKEN }} 69 | draft: true 70 | artifacts: | 71 | bitwarden-sdk-secrets-${{ env.PKG_VERSION }}.gem 72 | -------------------------------------------------------------------------------- /.github/workflows/release-rust-crates.yml: -------------------------------------------------------------------------------- 1 | name: Release Rust crates 2 | run-name: Release Rust crates ${{ inputs.release_type }} 3 | 4 | on: 5 | workflow_dispatch: 6 | inputs: 7 | release_type: 8 | description: "Release Options" 9 | required: true 10 | default: "Release" 11 | type: choice 12 | options: 13 | - Release 14 | - Dry Run 15 | 16 | jobs: 17 | setup: 18 | name: Setup 19 | runs-on: ubuntu-22.04 20 | outputs: 21 | release_version: ${{ steps.version.outputs.version }} 22 | steps: 23 | - name: Checkout repo 24 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 25 | 26 | - name: Branch check 27 | if: ${{ inputs.release_type != 'Dry Run' }} 28 | run: | 29 | if [[ "$GITHUB_REF" != "refs/heads/main" ]]; then 30 | echo "===================================" 31 | echo "[!] Can only release from the 'main' branch" 32 | echo "===================================" 33 | exit 1 34 | fi 35 | 36 | - name: Get version 37 | id: version 38 | run: | 39 | VERSION=$(grep -o '^version = ".*"' Cargo.toml | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+") 40 | echo "version=$VERSION" >> $GITHUB_OUTPUT 41 | 42 | - name: Create release 43 | if: ${{ inputs.release_type != 'Dry Run' }} 44 | uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0 45 | env: 46 | PKG_VERSION: ${{ steps.version.outputs.version }} 47 | with: 48 | commit: ${{ github.sha }} 49 | tag: rust-v${{ env.PKG_VERSION }} 50 | name: Rust crates v${{ env.PKG_VERSION }} 51 | body: "" 52 | token: ${{ secrets.GITHUB_TOKEN }} 53 | draft: true 54 | -------------------------------------------------------------------------------- /.github/workflows/release-wasm.yml: -------------------------------------------------------------------------------- 1 | name: Release @bitwarden/sdk-wasm 2 | run-name: Release @bitwarden/sdk-wasm ${{ inputs.release_type }} 3 | 4 | on: 5 | workflow_dispatch: 6 | inputs: 7 | release_type: 8 | description: "Release Options" 9 | required: true 10 | default: "Release" 11 | type: choice 12 | options: 13 | - Release 14 | - Dry Run 15 | 16 | defaults: 17 | run: 18 | working-directory: languages/js/wasm 19 | 20 | jobs: 21 | setup: 22 | name: Setup 23 | runs-on: ubuntu-22.04 24 | outputs: 25 | release_version: ${{ steps.version.outputs.version }} 26 | steps: 27 | - name: Checkout repo 28 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 29 | 30 | - name: Branch check 31 | if: ${{ inputs.release_type != 'Dry Run' }} 32 | run: | 33 | if [[ "$GITHUB_REF" != "refs/heads/main" ]]; then 34 | echo "===================================" 35 | echo "[!] Can only release from the 'main' branch" 36 | echo "===================================" 37 | exit 1 38 | fi 39 | 40 | - name: Check Release Version 41 | id: version 42 | uses: bitwarden/gh-actions/release_version-check@main 43 | with: 44 | release-type: ${{ inputs.release_type }} 45 | project-type: ts 46 | file: languages/js/wasm/package.json 47 | monorepo: false 48 | 49 | release: 50 | name: Release 51 | runs-on: ubuntu-22.04 52 | needs: setup 53 | steps: 54 | - name: Checkout repo 55 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 56 | 57 | - name: Download artifacts 58 | uses: dawidd6/action-download-artifact@bf251b5aa9c2f7eeb574a96ee720e24f801b7c11 # v6 59 | with: 60 | workflow: build-wasm.yml 61 | skip_unpack: true 62 | workflow_conclusion: success 63 | branch: main 64 | 65 | - name: Create GitHub release 66 | if: ${{ inputs.release_type != 'Dry Run' }} 67 | uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0 68 | env: 69 | PKG_VERSION: ${{ needs.setup.outputs.release_version }} 70 | with: 71 | commit: ${{ github.sha }} 72 | tag: wasm-v${{ env.PKG_VERSION }} 73 | name: WASM v${{ env.PKG_VERSION }} 74 | body: "" 75 | token: ${{ secrets.GITHUB_TOKEN }} 76 | draft: true 77 | artifacts: sdk-bitwarden-wasm.zip 78 | -------------------------------------------------------------------------------- /.github/workflows/rustdoc.yml: -------------------------------------------------------------------------------- 1 | name: Rustdoc 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | 7 | permissions: 8 | contents: read 9 | pages: write 10 | id-token: write 11 | 12 | concurrency: 13 | group: "pages" 14 | cancel-in-progress: false 15 | 16 | jobs: 17 | rustdoc: 18 | name: Rustdoc 19 | runs-on: ubuntu-24.04 20 | 21 | steps: 22 | - name: Checkout 23 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 24 | 25 | - name: Install rust 26 | uses: dtolnay/rust-toolchain@c5a29ddb4d9d194e7c84ec8c3fba61b1c31fee8c # stable 27 | with: 28 | toolchain: nightly 29 | 30 | - name: Build documentation 31 | env: 32 | RUSTDOCFLAGS: "--enable-index-page -Zunstable-options" 33 | run: cargo +nightly doc --no-deps --all-features --document-private-items 34 | 35 | - name: Deploy to GitHub Pages 36 | uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3.0.1 37 | with: 38 | path: ./target/doc 39 | 40 | deploy: 41 | environment: 42 | name: github-pages 43 | url: ${{ steps.deployment.outputs.page_url }} 44 | runs-on: ubuntu-24.04 45 | needs: rustdoc 46 | name: Deploy 47 | steps: 48 | - name: Deploy to GitHub Pages 49 | id: deployment 50 | uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5 51 | -------------------------------------------------------------------------------- /.github/workflows/scan.yml: -------------------------------------------------------------------------------- 1 | name: Scan 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - "main" 8 | - "rc" 9 | - "hotfix-rc" 10 | pull_request: 11 | types: [opened, synchronize, reopened] 12 | branches-ignore: 13 | - main 14 | pull_request_target: 15 | types: [opened, synchronize, reopened] 16 | branches: 17 | - "main" 18 | 19 | jobs: 20 | check-run: 21 | name: Check PR run 22 | uses: bitwarden/gh-actions/.github/workflows/check-run.yml@main 23 | 24 | sast: 25 | name: SAST scan 26 | runs-on: ubuntu-22.04 27 | needs: check-run 28 | permissions: 29 | contents: read 30 | pull-requests: write 31 | security-events: write 32 | 33 | steps: 34 | - name: Check out repo 35 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 36 | with: 37 | ref: ${{ github.event.pull_request.head.sha }} 38 | 39 | - name: Scan with Checkmarx 40 | uses: checkmarx/ast-github-action@f0869bd1a37fddc06499a096101e6c900e815d81 # 2.0.36 41 | env: 42 | INCREMENTAL: "${{ contains(github.event_name, 'pull_request') && '--sast-incremental' || '' }}" 43 | with: 44 | project_name: ${{ github.repository }} 45 | cx_tenant: ${{ secrets.CHECKMARX_TENANT }} 46 | base_uri: https://ast.checkmarx.net/ 47 | cx_client_id: ${{ secrets.CHECKMARX_CLIENT_ID }} 48 | cx_client_secret: ${{ secrets.CHECKMARX_SECRET }} 49 | additional_params: | 50 | --report-format sarif \ 51 | --filter "state=TO_VERIFY;PROPOSED_NOT_EXPLOITABLE;CONFIRMED;URGENT" \ 52 | --output-path . ${{ env.INCREMENTAL }} 53 | 54 | - name: Upload Checkmarx results to GitHub 55 | uses: github/codeql-action/upload-sarif@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0 56 | with: 57 | sarif_file: cx_result.sarif 58 | sha: ${{ contains(github.event_name, 'pull_request') && github.event.pull_request.head.sha || github.sha }} 59 | ref: ${{ contains(github.event_name, 'pull_request') && format('refs/pull/{0}/head', github.event.pull_request.number) || github.ref }} 60 | 61 | quality: 62 | name: Quality scan 63 | runs-on: ubuntu-22.04 64 | needs: check-run 65 | permissions: 66 | contents: read 67 | pull-requests: write 68 | 69 | steps: 70 | - name: Check out repo 71 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 72 | with: 73 | fetch-depth: 0 74 | ref: ${{ github.event.pull_request.head.sha }} 75 | 76 | - name: Scan with SonarCloud 77 | uses: sonarsource/sonarqube-scan-action@2500896589ef8f7247069a56136f8dc177c27ccf # v5.2.0 78 | env: 79 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 80 | with: 81 | args: > 82 | -Dsonar.organization=${{ github.repository_owner }} 83 | -Dsonar.projectKey=${{ github.repository_owner }}_${{ github.event.repository.name }} 84 | -Dsonar.exclusions=languages/** 85 | -Dsonar.pullrequest.key=${{ github.event.pull_request.number }} 86 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .DS_Store 3 | .pytest_cache 4 | .vscode/c_cpp_properties.json 5 | 6 | # Build results 7 | [Dd]ebug/ 8 | [Dd]ebugPublic/ 9 | [Rr]elease/ 10 | [Rr]eleases/ 11 | x64/ 12 | x86/ 13 | build/ 14 | bld/ 15 | [Oo]bj/ 16 | *.wasm 17 | 18 | # Binary files 19 | *.dylib 20 | *.a 21 | *.so 22 | *.dll 23 | *.class 24 | 25 | # Editor directories and files 26 | .idea 27 | xcuserdata/ 28 | 29 | # Added by cargo 30 | # 31 | # already existing elements were commented out 32 | 33 | #/target 34 | node_modules/ 35 | clients/python/env/ 36 | 37 | # Third party license 38 | THIRDPARTY.html 39 | 40 | # Node.js addon binary file, for the current running operating system. 41 | crates/bitwarden-napi/sdk-napi.*.node 42 | 43 | # Complied TypeScript client 44 | crates/bitwarden-napi/dist 45 | languages/js/sdk-client/dist/ 46 | 47 | # Schemas 48 | crates/bitwarden-napi/src-ts/bitwarden_client/schemas.ts 49 | languages/cpp/include/schemas.hpp 50 | languages/csharp/Bitwarden.Sdk/schemas.cs 51 | languages/go/schema.go 52 | languages/java/src/main/java/com/bitwarden/sdk/schema 53 | languages/js/sdk-client/src/schemas.ts 54 | languages/python/bitwarden_sdk/schemas.py 55 | support/schemas 56 | 57 | # Cmake build files 58 | languages/cpp/cmake-build-debug 59 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | target 2 | languages/* 3 | schemas 4 | /crates/bitwarden-napi/src-ts/bitwarden_client/schemas.ts 5 | about.hbs 6 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "overrides": [ 4 | { 5 | "files": "*.md", 6 | "options": { 7 | "proseWrap": "always" 8 | } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "esbenp.prettier-vscode", 5 | "ms-dotnettools.csharp", 6 | "MS-vsliveshare.vsliveshare", 7 | "rust-lang.rust-analyzer", 8 | "streetsidesoftware.code-spell-checker", 9 | "tamasfe.even-better-toml", 10 | "vadimcn.vscode-lldb" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "buildCsharp", 12 | "program": "${workspaceFolder}/languages/csharp/Bitwarden.Sdk/bin/Debug/net6.0/BitwardenSdk.dll", 13 | "args": [], 14 | "env": { 15 | "RUST_LOG": "debug" 16 | }, 17 | "cwd": "${workspaceFolder}", 18 | "stopAtEntry": false, 19 | "console": "internalConsole" 20 | }, 21 | { 22 | "name": "Python: Current File", 23 | "type": "python", 24 | "request": "launch", 25 | "program": "${file}", 26 | "console": "integratedTerminal", 27 | "preLaunchTask": "build python" 28 | }, 29 | { 30 | "type": "lldb", 31 | "request": "launch", 32 | "name": "Debug unit tests in library 'bitwardensdk'", 33 | "cargo": { 34 | "args": ["test", "--no-run", "--lib", "--package=bitwardensdk"], 35 | "filter": { 36 | "name": "bitwardensdk", 37 | "kind": "lib" 38 | } 39 | }, 40 | "args": [], 41 | "cwd": "${workspaceFolder}" 42 | } 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "bindgen", 4 | "Bitwarden", 5 | "Cdecl", 6 | "chrono", 7 | "cloc", 8 | "dealloc", 9 | "decryptable", 10 | "dylib", 11 | "encryptable", 12 | "Hkdf", 13 | "Hmac", 14 | "Maybeable", 15 | "Oaep", 16 | "Pbkdf", 17 | "PKCS8", 18 | "repr", 19 | "reprompt", 20 | "reqwest", 21 | "schemars", 22 | "totp", 23 | "uniffi", 24 | "wordlist", 25 | "Zeroize", 26 | "Zeroizing", 27 | "zxcvbn" 28 | ], 29 | "rust-analyzer.cargo.targetDir": true 30 | } 31 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "cargo", 6 | "command": "build", 7 | "problemMatcher": ["$rustc"], 8 | "options": { 9 | "cwd": "${workspaceFolder}/crates/bitwarden-c/" 10 | }, 11 | "group": { 12 | "kind": "build", 13 | "isDefault": true 14 | }, 15 | "label": "rust: bitwarden-c build" 16 | }, 17 | { 18 | "type": "cargo", 19 | "command": "build", 20 | "args": ["--release"], 21 | "options": { 22 | "cwd": "${workspaceFolder}/crates/bitwarden-c/" 23 | }, 24 | "problemMatcher": ["$rustc"], 25 | "label": "rust: bitwarden-c release build" 26 | }, 27 | { 28 | "label": "buildCsharp", 29 | "command": "dotnet", 30 | "type": "process", 31 | "args": [ 32 | "build", 33 | "${workspaceFolder}/languages/csharp/Bitwarden.Sdk/Bitwarden.Sdk.csproj", 34 | "/property:GenerateFullPaths=true", 35 | "/consoleloggerparameters:NoSummary" 36 | ], 37 | "problemMatcher": "$msCompile", 38 | "dependsOrder": "sequence", 39 | "dependsOn": ["rust: bitwarden-c build"] 40 | }, 41 | { 42 | "label": "build python", 43 | "command": "python3", 44 | "type": "shell", 45 | "args": ["setup.py", "develop"], 46 | "options": { 47 | "cwd": "${workspaceFolder}/languages/python" 48 | } 49 | }, 50 | { 51 | "label": "buildJava", 52 | "type": "shell", 53 | "command": "gradle", 54 | "args": ["build"], 55 | "dependsOrder": "sequence", 56 | "dependsOn": ["rust: bitwarden-c build"], 57 | "options": { 58 | "cwd": "${workspaceFolder}/languages/java" 59 | } 60 | } 61 | ] 62 | } 63 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | The changelog for the crates are located in the individual crates. 4 | 5 | - [`bitwarden`](./crates/bitwarden/CHANGELOG.md) 6 | - [`bws`](./crates/bws/CHANGELOG.md) 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = ["crates/*"] 4 | 5 | # Global settings for all crates should be defined here 6 | [workspace.package] 7 | # Update using `cargo set-version -p bitwarden ` 8 | version = "1.0.0" 9 | authors = ["Bitwarden Inc"] 10 | edition = "2021" 11 | # Note: Changing rust-version should be considered a breaking change 12 | rust-version = "1.75" 13 | homepage = "https://bitwarden.com" 14 | repository = "https://github.com/bitwarden/sdk-sm" 15 | license-file = "LICENSE" 16 | keywords = ["bitwarden"] 17 | 18 | # Define dependencies that are expected to be consistent across all crates 19 | [workspace.dependencies] 20 | bitwarden = { path = "crates/bitwarden", version = "=1.0.0" } 21 | bitwarden-cli = { version = "=1.0.0" } 22 | bitwarden-core = { version = "=1.0.0" } 23 | bitwarden-crypto = { version = "=1.0.0" } 24 | bitwarden-generators = { version = "=1.0.0" } 25 | bitwarden-sm = { version = "=1.0.0" } 26 | 27 | log = "0.4.20" 28 | schemars = { version = ">=0.8.9, <0.9", features = ["uuid1", "chrono"] } 29 | tokio = { version = "1.36.0", features = ["macros"] } 30 | 31 | [workspace.lints.clippy] 32 | unused_async = "deny" 33 | unwrap_used = "deny" 34 | 35 | # Compile all dependencies with some optimizations when building this crate on debug 36 | # This slows down clean builds by about 50%, but the resulting binaries can be orders of magnitude faster 37 | # As clean builds won't occur very often, this won't slow down the development process 38 | [profile.dev.package."*"] 39 | opt-level = 2 40 | 41 | # Turn on a small amount of optimisation in development mode. This might interfere when trying to use a debugger 42 | # if the compiler decides to optimize some code away, if that's the case, it can be set to 0 or commented out 43 | [profile.dev] 44 | opt-level = 1 45 | 46 | # Turn on LTO on release mode 47 | [profile.release] 48 | lto = "thin" 49 | codegen-units = 1 50 | 51 | # Turn off LTO on release mode for windows 52 | # This is a workaround until this is fixed: https://github.com/rustls/rustls-platform-verifier/issues/141 53 | [profile.release-windows] 54 | inherits = "release" 55 | lto = "off" 56 | 57 | # Stripping the binary reduces the size by ~30%, but the stacktraces won't be usable anymore. 58 | # This is fine as long as we don't have any unhandled panics, but let's keep it disabled for now 59 | # strip = true 60 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | Bitwarden believes that working with security researchers across the globe is crucial to keeping our 2 | users safe. If you believe you've found a security issue in our product or service, we encourage you 3 | to please submit a report through our [HackerOne Program](https://hackerone.com/bitwarden/). We 4 | welcome working with you to resolve the issue promptly. Thanks in advance! 5 | 6 | # Disclosure Policy 7 | 8 | - Let us know as soon as possible upon discovery of a potential security issue, and we'll make every 9 | effort to quickly resolve the issue. 10 | - Provide us a reasonable amount of time to resolve the issue before any disclosure to the public or 11 | a third-party. We may publicly disclose the issue before resolving it, if appropriate. 12 | - Make a good faith effort to avoid privacy violations, destruction of data, and interruption or 13 | degradation of our service. Only interact with accounts you own or with explicit permission of the 14 | account holder. 15 | - If you would like to encrypt your report, please use the PGP key with long ID 16 | `0xDE6887086F892325FEC04CC0D847525B6931381F` (available in the public keyserver pool). 17 | 18 | While researching, we'd like to ask you to refrain from: 19 | 20 | - Denial of service 21 | - Spamming 22 | - Social engineering (including phishing) of Bitwarden staff or contractors 23 | - Any physical attempts against Bitwarden property or data centers 24 | 25 | # We want to help you! 26 | 27 | If you have something that you feel is close to exploitation, or if you'd like some information 28 | regarding the internal API, or generally have any questions regarding the app that would help in 29 | your efforts, please email us at https://bitwarden.com/contact and ask for that information. As 30 | stated above, Bitwarden wants to help you find issues, and is more than willing to help. 31 | 32 | Thank you for helping keep Bitwarden and our users safe! 33 | -------------------------------------------------------------------------------- /about.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 36 | 37 | 38 | 39 |
40 |
41 |

Third Party Licenses

42 |

This page lists the licenses of the projects used in $NAME$.

43 |
44 | 45 |

Overview of licenses:

46 |
    47 | {{#each overview}} 48 |
  • {{name}} ({{count}})
  • 49 | {{/each}} 50 |
51 | 52 |

All license text:

53 |
    54 | {{#each licenses}} 55 |
  • 56 |

    {{name}}

    57 |

    Used by:

    58 | 63 |
    {{text}}
    64 |
  • 65 | {{/each}} 66 |
67 |
68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /about.toml: -------------------------------------------------------------------------------- 1 | accepted = [ 2 | "MIT", 3 | "ISC", 4 | "BSD-2-Clause", 5 | "BSD-3-Clause", 6 | "CC0-1.0", 7 | "Apache-2.0", 8 | "MPL-2.0", 9 | "LGPL-3.0", 10 | "Unicode-DFS-2016", 11 | "OpenSSL", 12 | ] 13 | 14 | # Ring has all the licenses combined into a single file, which causes cargo about to 15 | # be confused about it. Thankfully it includes a workaround for this that we can enable. 16 | workarounds = ["ring"] 17 | -------------------------------------------------------------------------------- /bacon.toml: -------------------------------------------------------------------------------- 1 | # This is a configuration file for the bacon tool 2 | # 3 | # Bacon repository: https://github.com/Canop/bacon 4 | # Complete help on configuration: https://dystroy.org/bacon/config/ 5 | # You can also check bacon's own bacon.toml file 6 | # as an example: https://github.com/Canop/bacon/blob/main/bacon.toml 7 | 8 | default_job = "check" 9 | 10 | [jobs.check] 11 | command = ["cargo", "check", "--color", "always"] 12 | need_stdout = false 13 | 14 | [jobs.check-all] 15 | command = ["cargo", "check", "--all-targets", "--color", "always"] 16 | need_stdout = false 17 | 18 | [jobs.clippy] 19 | command = ["cargo", "clippy", "--all-targets", "--color", "always"] 20 | need_stdout = false 21 | 22 | [jobs.test] 23 | command = [ 24 | "cargo", 25 | "test", 26 | "--all-features", 27 | "--color", 28 | "always", 29 | "--", 30 | "--color", 31 | "always", # see https://github.com/Canop/bacon/issues/124 32 | ] 33 | need_stdout = true 34 | 35 | [jobs.doc] 36 | command = ["cargo", "doc", "--color", "always", "--no-deps"] 37 | need_stdout = false 38 | 39 | # If the doc compiles, then it opens in your browser and bacon switches 40 | # to the previous job 41 | [jobs.doc-open] 42 | command = ["cargo", "doc", "--color", "always", "--no-deps", "--open"] 43 | need_stdout = false 44 | on_success = "back" # so that we don't open the browser at each change 45 | 46 | # You may define here keybindings that would be specific to 47 | # a project, for example a shortcut to launch a specific job. 48 | # Shortcuts to internal functions (scrolling, toggling, etc.) 49 | # should go in your personal global prefs.toml file instead. 50 | [keybindings] 51 | # alt-m = "job:my-job" 52 | -------------------------------------------------------------------------------- /clippy.toml: -------------------------------------------------------------------------------- 1 | allow-unwrap-in-tests=true 2 | allow-expect-in-tests=true 3 | -------------------------------------------------------------------------------- /crates/bitwarden-c/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bitwarden-c" 3 | version = "0.1.0" 4 | publish = false 5 | 6 | authors.workspace = true 7 | edition.workspace = true 8 | rust-version.workspace = true 9 | homepage.workspace = true 10 | repository.workspace = true 11 | license-file.workspace = true 12 | 13 | [lib] 14 | crate-type = ["staticlib", "cdylib"] 15 | bench = false 16 | 17 | [target.'cfg(not(target_arch="wasm32"))'.dependencies] 18 | tokio = { version = ">=1.28.2, <2.0", features = ["rt-multi-thread", "macros"] } 19 | bitwarden-json = { path = "../bitwarden-json", features = ["secrets"] } 20 | 21 | [dependencies] 22 | env_logger = ">=0.10.0, <0.12" 23 | 24 | [lints] 25 | workspace = true 26 | -------------------------------------------------------------------------------- /crates/bitwarden-c/src/lib.rs: -------------------------------------------------------------------------------- 1 | // These are the C bindings, we're going to have to use unsafe raw pointers 2 | #![allow(clippy::not_unsafe_ptr_arg_deref)] 3 | 4 | #[cfg(not(target_arch = "wasm32"))] 5 | pub use c::*; 6 | 7 | #[cfg(not(target_arch = "wasm32"))] 8 | mod c; 9 | mod macros; 10 | -------------------------------------------------------------------------------- /crates/bitwarden-c/src/macros/ffi.rs: -------------------------------------------------------------------------------- 1 | // Get a reference to an object from a pointer 2 | #[macro_export] 3 | macro_rules! ffi_ref { 4 | ($name:ident) => {{ 5 | assert!(!$name.is_null()); 6 | &*$name 7 | }}; 8 | } 9 | 10 | // Returns a raw pointer from an object 11 | #[macro_export] 12 | macro_rules! box_ptr { 13 | ($x:expr) => { 14 | Box::into_raw(Box::new($x)) 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /crates/bitwarden-c/src/macros/mod.rs: -------------------------------------------------------------------------------- 1 | mod ffi; 2 | -------------------------------------------------------------------------------- /crates/bitwarden-json/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bitwarden-json" 3 | version = "0.3.0" 4 | description = """ 5 | JSON bindings for the Bitwarden Secret Manager SDK 6 | """ 7 | keywords = ["bitwarden", "secrets-manager"] 8 | categories = ["api-bindings"] 9 | publish = false 10 | 11 | authors.workspace = true 12 | edition.workspace = true 13 | rust-version.workspace = true 14 | homepage.workspace = true 15 | repository.workspace = true 16 | license-file.workspace = true 17 | 18 | [features] 19 | secrets = ["bitwarden/secrets"] # Secrets manager API 20 | 21 | [dependencies] 22 | bitwarden = { workspace = true } 23 | log = ">=0.4.18, <0.5" 24 | schemars = { workspace = true } 25 | serde = { version = ">=1.0, <2.0", features = ["derive"] } 26 | serde_json = ">=1.0.96, <2.0" 27 | 28 | [target.'cfg(debug_assertions)'.dependencies] 29 | tokio = { version = "1.36.0", features = ["time"] } 30 | 31 | [lints] 32 | workspace = true 33 | -------------------------------------------------------------------------------- /crates/bitwarden-json/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod client; 2 | pub mod command; 3 | pub mod response; 4 | -------------------------------------------------------------------------------- /crates/bitwarden-json/src/response.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | use schemars::JsonSchema; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Serialize, Deserialize, Debug, JsonSchema)] 7 | #[serde(rename_all = "camelCase", deny_unknown_fields)] 8 | pub struct Response { 9 | /// Whether or not the SDK request succeeded. 10 | pub success: bool, 11 | /// A message for any error that may occur. Populated if `success` is false. 12 | pub error_message: Option, 13 | /// The response data. Populated if `success` is true. 14 | pub data: Option, 15 | } 16 | 17 | impl Response { 18 | pub fn new(response: Result) -> Self { 19 | match response { 20 | Ok(data) => Self { 21 | success: true, 22 | error_message: None, 23 | data: Some(data), 24 | }, 25 | Err(err) => Self { 26 | success: false, 27 | error_message: Some(err.to_string()), 28 | data: None, 29 | }, 30 | } 31 | } 32 | } 33 | 34 | impl Response<()> { 35 | pub fn error(message: String) -> Self { 36 | Self { 37 | success: false, 38 | error_message: Some(message), 39 | data: None, 40 | } 41 | } 42 | } 43 | 44 | pub(crate) trait ResponseIntoString { 45 | fn into_string(self) -> String; 46 | } 47 | 48 | impl ResponseIntoString for Result { 49 | fn into_string(self) -> String { 50 | Response::new(self).into_string() 51 | } 52 | } 53 | 54 | impl ResponseIntoString for Response { 55 | fn into_string(self) -> String { 56 | match serde_json::to_string(&self) { 57 | Ok(ser) => ser, 58 | Err(e) => { 59 | let error = Response::error(format!("Failed to serialize Response: {}", e)); 60 | serde_json::to_string(&error).expect("Serialize should be infallible") 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /crates/bitwarden-napi/.npmignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | .cargo 4 | .github 5 | npm 6 | .eslintrc 7 | .prettierignore 8 | rustfmt.toml 9 | yarn.lock 10 | *.node 11 | .yarn 12 | __test__ 13 | renovate.json 14 | -------------------------------------------------------------------------------- /crates/bitwarden-napi/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bitwarden-napi" 3 | version = "1.0.0" 4 | description = """ 5 | N-API bindings for the Bitwarden Secrets Manager SDK 6 | """ 7 | keywords = ["bitwarden", "secrets-manager"] 8 | publish = false 9 | 10 | authors.workspace = true 11 | edition.workspace = true 12 | rust-version.workspace = true 13 | homepage.workspace = true 14 | repository.workspace = true 15 | license-file.workspace = true 16 | 17 | [lib] 18 | crate-type = ["cdylib", "rlib"] 19 | 20 | [dependencies] 21 | bitwarden-json = { path = "../bitwarden-json", version = "0.3.0", features = [ 22 | "secrets", 23 | ] } 24 | env_logger = "0.11.1" 25 | log = { workspace = true } 26 | napi = { version = "2", features = ["async"] } 27 | napi-derive = "2" 28 | 29 | [build-dependencies] 30 | napi-build = "2.1.0" 31 | 32 | [lints] 33 | workspace = true 34 | -------------------------------------------------------------------------------- /crates/bitwarden-napi/README.md: -------------------------------------------------------------------------------- 1 | ## Bitwarden Secrets Manager SDK 2 | 3 | Node-API bindings for interacting with the Bitwarden Secrets Manager. This is a beta release and 4 | might be missing some functionality. 5 | 6 | ## Getting started 7 | 8 | ```ts 9 | import { BitwardenClient, ClientSettings, DeviceType, LogLevel } from "@bitwarden/sdk-napi"; 10 | 11 | // Optional settings 12 | const settings: ClientSettings = { 13 | apiUrl: "https://api.bitwarden.com", 14 | identityUrl: "https://identity.bitwarden.com", 15 | userAgent: "Bitwarden SDK", 16 | deviceType: DeviceType.SDK, 17 | }; 18 | 19 | const accessToken = "-- REDACTED --"; 20 | const stateFile = "some/path/to/state/file"; 21 | 22 | const client = new BitwardenClient(settings, LogLevel.Info); 23 | 24 | // Authenticating using a machine account access token 25 | await client.auth().loginAccessToken(accessToken, stateFile); 26 | 27 | // List secrets 28 | const secrets = await client.secrets().list(); 29 | 30 | // Get a specific secret 31 | const secret = await client.secrets().get("secret-id"); 32 | ``` 33 | -------------------------------------------------------------------------------- /crates/bitwarden-napi/binding.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | 4 | /* auto-generated by NAPI-RS */ 5 | 6 | export const enum LogLevel { 7 | Trace = 0, 8 | Debug = 1, 9 | Info = 2, 10 | Warn = 3, 11 | Error = 4, 12 | } 13 | export declare class BitwardenClient { 14 | constructor(settingsInput?: string | undefined | null, logLevel?: LogLevel | undefined | null); 15 | runCommand(commandInput: string): Promise; 16 | } 17 | -------------------------------------------------------------------------------- /crates/bitwarden-napi/build.rs: -------------------------------------------------------------------------------- 1 | extern crate napi_build; 2 | 3 | fn main() { 4 | napi_build::setup(); 5 | } 6 | -------------------------------------------------------------------------------- /crates/bitwarden-napi/npm/darwin-arm64/README.md: -------------------------------------------------------------------------------- 1 | # `@bitwarden/sdk-napi-darwin-arm64` 2 | 3 | This is the **aarch64-apple-darwin** binary for `@bitwarden/sdk-napi` 4 | -------------------------------------------------------------------------------- /crates/bitwarden-napi/npm/darwin-arm64/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bitwarden/sdk-napi-darwin-arm64", 3 | "version": "1.0.0", 4 | "homepage": "https://github.com/bitwarden/sdk-sm#readme", 5 | "bugs": { 6 | "url": "https://github.com/bitwarden/sdk-sm/issues" 7 | }, 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/bitwarden/sdk-sm.git" 11 | }, 12 | "license": "SEE LICENSE IN LICENSE", 13 | "author": "Bitwarden Inc. (https://bitwarden.com)", 14 | "main": "sdk-napi.darwin-arm64.node", 15 | "files": [ 16 | "sdk-napi.darwin-arm64.node" 17 | ], 18 | "engines": { 19 | "node": ">= 10" 20 | }, 21 | "os": [ 22 | "darwin" 23 | ], 24 | "cpu": [ 25 | "arm64" 26 | ], 27 | "publishConfig": { 28 | "access": "public", 29 | "registry": "https://registry.npmjs.org/" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /crates/bitwarden-napi/npm/darwin-x64/README.md: -------------------------------------------------------------------------------- 1 | # `@bitwarden/sdk-napi-darwin-x64` 2 | 3 | This is the **x86_64-apple-darwin** binary for `@bitwarden/sdk-napi` 4 | -------------------------------------------------------------------------------- /crates/bitwarden-napi/npm/darwin-x64/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bitwarden/sdk-napi-darwin-x64", 3 | "version": "1.0.0", 4 | "homepage": "https://github.com/bitwarden/sdk-sm#readme", 5 | "bugs": { 6 | "url": "https://github.com/bitwarden/sdk-sm/issues" 7 | }, 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/bitwarden/sdk-sm.git" 11 | }, 12 | "license": "SEE LICENSE IN LICENSE", 13 | "author": "Bitwarden Inc. (https://bitwarden.com)", 14 | "main": "sdk-napi.darwin-x64.node", 15 | "files": [ 16 | "sdk-napi.darwin-x64.node" 17 | ], 18 | "engines": { 19 | "node": ">= 10" 20 | }, 21 | "os": [ 22 | "darwin" 23 | ], 24 | "cpu": [ 25 | "x64" 26 | ], 27 | "publishConfig": { 28 | "access": "public", 29 | "registry": "https://registry.npmjs.org/" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /crates/bitwarden-napi/npm/linux-x64-gnu/README.md: -------------------------------------------------------------------------------- 1 | # `@bitwarden/sdk-napi-linux-x64-gnu` 2 | 3 | This is the **x86_64-unknown-linux-gnu** binary for `@bitwarden/sdk-napi` 4 | -------------------------------------------------------------------------------- /crates/bitwarden-napi/npm/linux-x64-gnu/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bitwarden/sdk-napi-linux-x64-gnu", 3 | "version": "1.0.0", 4 | "homepage": "https://github.com/bitwarden/sdk-sm#readme", 5 | "bugs": { 6 | "url": "https://github.com/bitwarden/sdk-sm/issues" 7 | }, 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/bitwarden/sdk-sm.git" 11 | }, 12 | "license": "SEE LICENSE IN LICENSE", 13 | "author": "Bitwarden Inc. (https://bitwarden.com)", 14 | "main": "sdk-napi.linux-x64-gnu.node", 15 | "files": [ 16 | "sdk-napi.linux-x64-gnu.node" 17 | ], 18 | "engines": { 19 | "node": ">= 10" 20 | }, 21 | "os": [ 22 | "linux" 23 | ], 24 | "cpu": [ 25 | "x64" 26 | ], 27 | "libc": [ 28 | "glibc" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /crates/bitwarden-napi/npm/win32-x64-msvc/README.md: -------------------------------------------------------------------------------- 1 | # `@bitwarden/sdk-napi-win32-x64-msvc` 2 | 3 | This is the **x86_64-pc-windows-msvc** binary for `@bitwarden/sdk-napi` 4 | -------------------------------------------------------------------------------- /crates/bitwarden-napi/npm/win32-x64-msvc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bitwarden/sdk-napi-win32-x64-msvc", 3 | "version": "1.0.0", 4 | "homepage": "https://github.com/bitwarden/sdk-sm#readme", 5 | "bugs": { 6 | "url": "https://github.com/bitwarden/sdk-sm/issues" 7 | }, 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/bitwarden/sdk-sm.git" 11 | }, 12 | "license": "SEE LICENSE IN LICENSE", 13 | "author": "Bitwarden Inc. (https://bitwarden.com)", 14 | "main": "sdk-napi.win32-x64-msvc.node", 15 | "files": [ 16 | "sdk-napi.win32-x64-msvc.node" 17 | ], 18 | "engines": { 19 | "node": ">= 10" 20 | }, 21 | "os": [ 22 | "win32" 23 | ], 24 | "cpu": [ 25 | "x64" 26 | ], 27 | "publishConfig": { 28 | "access": "public", 29 | "registry": "https://registry.npmjs.org/" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /crates/bitwarden-napi/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bitwarden/sdk-napi", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@bitwarden/sdk-napi", 9 | "version": "1.0.0", 10 | "license": "SEE LICENSE IN LICENSE", 11 | "devDependencies": { 12 | "@napi-rs/cli": "2.18.4", 13 | "typescript": "5.5.4" 14 | }, 15 | "engines": { 16 | "node": ">= 10" 17 | } 18 | }, 19 | "node_modules/@napi-rs/cli": { 20 | "version": "2.18.4", 21 | "resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-2.18.4.tgz", 22 | "integrity": "sha512-SgJeA4df9DE2iAEpr3M2H0OKl/yjtg1BnRI5/JyowS71tUWhrfSu2LT0V3vlHET+g1hBVlrO60PmEXwUEKp8Mg==", 23 | "dev": true, 24 | "license": "MIT", 25 | "bin": { 26 | "napi": "scripts/index.js" 27 | }, 28 | "engines": { 29 | "node": ">= 10" 30 | }, 31 | "funding": { 32 | "type": "github", 33 | "url": "https://github.com/sponsors/Brooooooklyn" 34 | } 35 | }, 36 | "node_modules/typescript": { 37 | "version": "5.5.4", 38 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", 39 | "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", 40 | "dev": true, 41 | "license": "Apache-2.0", 42 | "bin": { 43 | "tsc": "bin/tsc", 44 | "tsserver": "bin/tsserver" 45 | }, 46 | "engines": { 47 | "node": ">=14.17" 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /crates/bitwarden-napi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bitwarden/sdk-napi", 3 | "version": "1.0.0", 4 | "homepage": "https://github.com/bitwarden/sdk-sm#readme", 5 | "bugs": { 6 | "url": "https://github.com/bitwarden/sdk-sm/issues" 7 | }, 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/bitwarden/sdk-sm.git" 11 | }, 12 | "license": "SEE LICENSE IN LICENSE", 13 | "author": "Bitwarden Inc. (https://bitwarden.com)", 14 | "main": "./dist/index.js", 15 | "types": "./dist/index.d.ts", 16 | "files": [ 17 | "binding.js", 18 | "binding.d.ts", 19 | "./dist/", 20 | "./dist/bitwarden_client/" 21 | ], 22 | "scripts": { 23 | "artifacts": "napi artifacts", 24 | "build": "napi build --platform --release --js binding.js --dts binding.d.ts && tsc", 25 | "build-arm64": "napi build --target aarch64-apple-darwin --platform --release --js binding.js --dts binding.d.ts && tsc", 26 | "build:debug": "napi build --platform", 27 | "prepublishOnly": "napi prepublish --skip-gh-release", 28 | "tsc": "tsc", 29 | "version": "napi version" 30 | }, 31 | "devDependencies": { 32 | "@napi-rs/cli": "2.18.4", 33 | "typescript": "5.5.4" 34 | }, 35 | "engines": { 36 | "node": ">= 10" 37 | }, 38 | "publishConfig": { 39 | "access": "public", 40 | "registry": "https://registry.npmjs.org/" 41 | }, 42 | "napi": { 43 | "name": "sdk-napi", 44 | "triples": { 45 | "additional": [ 46 | "aarch64-apple-darwin" 47 | ] 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /crates/bitwarden-napi/src-ts/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./bitwarden_client/index"; 2 | export * from "./bitwarden_client/schemas"; 3 | export { LogLevel } from "../binding"; 4 | -------------------------------------------------------------------------------- /crates/bitwarden-napi/src/client.rs: -------------------------------------------------------------------------------- 1 | extern crate log; 2 | 3 | use bitwarden_json::client::Client as JsonClient; 4 | use napi_derive::napi; 5 | 6 | #[napi] 7 | pub enum LogLevel { 8 | Trace, 9 | Debug, 10 | Info, 11 | Warn, 12 | Error, 13 | } 14 | 15 | fn convert_level(level: LogLevel) -> log::LevelFilter { 16 | match level { 17 | LogLevel::Trace => log::LevelFilter::Trace, 18 | LogLevel::Debug => log::LevelFilter::Debug, 19 | LogLevel::Info => log::LevelFilter::Info, 20 | LogLevel::Warn => log::LevelFilter::Warn, 21 | LogLevel::Error => log::LevelFilter::Error, 22 | } 23 | } 24 | 25 | #[napi] 26 | pub struct BitwardenClient(JsonClient); 27 | 28 | #[napi] 29 | impl BitwardenClient { 30 | #[napi(constructor)] 31 | pub fn new(settings_input: Option, log_level: Option) -> Self { 32 | // This will only fail if another logger was already initialized, so we can ignore the 33 | // result 34 | let _ = env_logger::Builder::from_default_env() 35 | .filter_level(convert_level(log_level.unwrap_or(LogLevel::Info))) 36 | .try_init(); 37 | Self(bitwarden_json::client::Client::new(settings_input)) 38 | } 39 | 40 | #[napi] 41 | pub async fn run_command(&self, command_input: String) -> String { 42 | self.0.run_command(&command_input).await 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /crates/bitwarden-napi/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(target_arch = "wasm32"))] 2 | mod client; 3 | -------------------------------------------------------------------------------- /crates/bitwarden-napi/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "outDir": "./dist", 6 | "rootDir": "./src-ts", 7 | "strict": true, 8 | "noImplicitAny": true, 9 | "esModuleInterop": true, 10 | "declaration": true 11 | }, 12 | "include": ["src-ts", "src-ts/bitwarden_client", "src-ts/index.ts"] 13 | } 14 | -------------------------------------------------------------------------------- /crates/bitwarden-py/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bitwarden-py" 3 | version = "0.1.0" 4 | publish = false 5 | 6 | authors.workspace = true 7 | edition.workspace = true 8 | rust-version.workspace = true 9 | homepage.workspace = true 10 | repository.workspace = true 11 | license-file.workspace = true 12 | keywords.workspace = true 13 | 14 | [lib] 15 | name = "bitwarden_py" 16 | crate-type = ["cdylib"] 17 | 18 | [dependencies] 19 | bitwarden-json = { path = "../bitwarden-json", features = ["secrets"] } 20 | pyo3 = { version = "0.22.1", features = ["extension-module"] } 21 | pyo3-log = "0.11.0" 22 | 23 | [build-dependencies] 24 | pyo3-build-config = { version = "0.22.1" } 25 | 26 | [target.'cfg(not(target_arch="wasm32"))'.dependencies] 27 | tokio = { workspace = true, features = ["rt-multi-thread"] } 28 | 29 | [lints] 30 | workspace = true 31 | -------------------------------------------------------------------------------- /crates/bitwarden-py/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include Cargo.toml 2 | recursive-include src * 3 | -------------------------------------------------------------------------------- /crates/bitwarden-py/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | pyo3_build_config::add_extension_module_link_args(); 3 | } 4 | -------------------------------------------------------------------------------- /crates/bitwarden-py/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel", "setuptools-rust"] 3 | -------------------------------------------------------------------------------- /crates/bitwarden-py/src/client.rs: -------------------------------------------------------------------------------- 1 | use bitwarden_json::client::Client as JsonClient; 2 | use pyo3::prelude::*; 3 | 4 | #[pyclass] 5 | pub struct BitwardenClient(tokio::runtime::Runtime, JsonClient); 6 | 7 | #[pymethods] 8 | impl BitwardenClient { 9 | #[new] 10 | #[pyo3(signature = (settings_string=None))] 11 | pub fn new(settings_string: Option) -> Self { 12 | // This will only fail if another logger was already initialized, so we can ignore the 13 | // result 14 | let _ = pyo3_log::try_init(); 15 | 16 | let runtime = tokio::runtime::Builder::new_multi_thread() 17 | .enable_all() 18 | .build() 19 | .expect("Failed to build tokio runtime"); 20 | 21 | Self(runtime, JsonClient::new(settings_string)) 22 | } 23 | 24 | #[pyo3(text_signature = "($self, command_input)")] 25 | fn run_command(&self, command_input: String) -> String { 26 | self.0.block_on(self.1.run_command(&command_input)) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /crates/bitwarden-py/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(target_arch = "wasm32"))] 2 | mod client; 3 | 4 | #[cfg(not(target_arch = "wasm32"))] 5 | mod python_module; 6 | -------------------------------------------------------------------------------- /crates/bitwarden-py/src/python_module.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | use crate::client::BitwardenClient; 4 | 5 | #[pymodule] 6 | fn bitwarden_py(m: &Bound<'_, PyModule>) -> PyResult<()> { 7 | m.add_class::()?; 8 | Ok(()) 9 | } 10 | -------------------------------------------------------------------------------- /crates/bitwarden-wasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bitwarden-wasm" 3 | version = "0.1.0" 4 | publish = false 5 | 6 | authors.workspace = true 7 | edition.workspace = true 8 | rust-version.workspace = true 9 | homepage.workspace = true 10 | repository.workspace = true 11 | license-file.workspace = true 12 | keywords.workspace = true 13 | 14 | [lib] 15 | crate-type = ["cdylib"] 16 | 17 | [dependencies] 18 | argon2 = { version = ">=0.5.0, <0.6", features = [ 19 | "alloc", 20 | "zeroize", 21 | ], default-features = false } 22 | bitwarden-json = { path = "../bitwarden-json", features = ["secrets"] } 23 | console_error_panic_hook = "0.1.7" 24 | console_log = { version = "1.0.0", features = ["color"] } 25 | js-sys = "0.3.68" 26 | log = "0.4.20" 27 | serde = { version = "1.0.196", features = ["derive"] } 28 | # When upgrading wasm-bindgen, make sure to update the version in the workflows! 29 | wasm-bindgen = { version = "=0.2.95", features = ["serde-serialize"] } 30 | wasm-bindgen-futures = "0.4.41" 31 | 32 | [dev-dependencies] 33 | wasm-bindgen-test = "0.3.41" 34 | 35 | [target.'cfg(target_arch = "wasm32")'.dependencies] 36 | chrono = { version = ">=0.4.26, <0.5", features = [ 37 | "clock", 38 | "serde", 39 | "std", 40 | "wasmbind", 41 | ], default-features = false } 42 | 43 | [lints] 44 | workspace = true 45 | -------------------------------------------------------------------------------- /crates/bitwarden-wasm/README.md: -------------------------------------------------------------------------------- 1 | # Bitwarden-wasm 2 | 3 | Requirements: 4 | 5 | - `wasm32-unknown-unknown` rust target. 6 | - `wasm-bindgen-cli` installed. 7 | - `binaryen` installed for `wasm-opt` and `wasm2js`. 8 | 9 | ```bash 10 | rustup target add wasm32-unknown-unknown 11 | cargo install -f wasm-bindgen-cli 12 | brew install binaryen 13 | ``` 14 | 15 | #### Build 16 | 17 | ```bash 18 | # dev 19 | ./build.sh 20 | 21 | # release 22 | ./build.sh -r 23 | ``` 24 | -------------------------------------------------------------------------------- /crates/bitwarden-wasm/build.sh: -------------------------------------------------------------------------------- 1 | # Move to the root of the repository 2 | cd "$(dirname "$0")" 3 | cd ../../ 4 | 5 | if [ "$1" != "-r" ]; then 6 | # Dev 7 | cargo build -p bitwarden-wasm --target wasm32-unknown-unknown 8 | wasm-bindgen --target bundler --out-dir languages/js/wasm ./target/wasm32-unknown-unknown/debug/bitwarden_wasm.wasm 9 | wasm-bindgen --target nodejs --out-dir languages/js/wasm/node ./target/wasm32-unknown-unknown/debug/bitwarden_wasm.wasm 10 | else 11 | # Release 12 | cargo build -p bitwarden-wasm --target wasm32-unknown-unknown --release 13 | wasm-bindgen --target bundler --out-dir languages/js/wasm ./target/wasm32-unknown-unknown/release/bitwarden_wasm.wasm 14 | wasm-bindgen --target nodejs --out-dir languages/js/wasm/node ./target/wasm32-unknown-unknown/release/bitwarden_wasm.wasm 15 | fi 16 | 17 | # Optimize size 18 | wasm-opt -Os ./languages/js/wasm/bitwarden_wasm_bg.wasm -o ./languages/js/wasm/bitwarden_wasm_bg.wasm 19 | wasm-opt -Os ./languages/js/wasm/node/bitwarden_wasm_bg.wasm -o ./languages/js/wasm/node/bitwarden_wasm_bg.wasm 20 | 21 | # Transpile to JS 22 | wasm2js ./languages/js/wasm/bitwarden_wasm_bg.wasm -o ./languages/js/wasm/bitwarden_wasm_bg.wasm.js 23 | npx terser ./languages/js/wasm/bitwarden_wasm_bg.wasm.js -o ./languages/js/wasm/bitwarden_wasm_bg.wasm.js 24 | -------------------------------------------------------------------------------- /crates/bitwarden-wasm/src/client.rs: -------------------------------------------------------------------------------- 1 | extern crate console_error_panic_hook; 2 | use std::rc::Rc; 3 | 4 | use argon2::{Algorithm, Argon2, Params, Version}; 5 | use bitwarden_json::client::Client as JsonClient; 6 | use js_sys::Promise; 7 | use log::{set_max_level, Level}; 8 | use wasm_bindgen::prelude::*; 9 | use wasm_bindgen_futures::future_to_promise; 10 | 11 | #[wasm_bindgen] 12 | pub enum LogLevel { 13 | Trace, 14 | Debug, 15 | Info, 16 | Warn, 17 | Error, 18 | } 19 | 20 | fn convert_level(level: LogLevel) -> Level { 21 | match level { 22 | LogLevel::Trace => Level::Trace, 23 | LogLevel::Debug => Level::Debug, 24 | LogLevel::Info => Level::Info, 25 | LogLevel::Warn => Level::Warn, 26 | LogLevel::Error => Level::Error, 27 | } 28 | } 29 | 30 | // Rc<...> is to avoid needing to take ownership of the Client during our async run_command 31 | // function https://github.com/rustwasm/wasm-bindgen/issues/2195#issuecomment-799588401 32 | #[wasm_bindgen] 33 | pub struct BitwardenClient(Rc); 34 | 35 | #[wasm_bindgen] 36 | impl BitwardenClient { 37 | #[wasm_bindgen(constructor)] 38 | pub fn new(settings_input: Option, log_level: Option) -> Self { 39 | console_error_panic_hook::set_once(); 40 | let log_level = convert_level(log_level.unwrap_or(LogLevel::Info)); 41 | if let Err(_e) = console_log::init_with_level(log_level) { 42 | set_max_level(log_level.to_level_filter()) 43 | } 44 | 45 | Self(Rc::new(bitwarden_json::client::Client::new(settings_input))) 46 | } 47 | 48 | #[wasm_bindgen] 49 | pub fn run_command(&self, js_input: String) -> Promise { 50 | let rc = self.0.clone(); 51 | future_to_promise(async move { 52 | let result = rc.run_command(&js_input).await; 53 | Ok(result.into()) 54 | }) 55 | } 56 | } 57 | 58 | #[wasm_bindgen] 59 | pub fn argon2( 60 | password: &[u8], 61 | salt: &[u8], 62 | iterations: u32, 63 | memory: u32, 64 | parallelism: u32, 65 | ) -> Result, JsError> { 66 | let argon = Argon2::new( 67 | Algorithm::Argon2id, 68 | Version::V0x13, 69 | Params::new( 70 | memory * 1024, // Convert MiB to KiB 71 | iterations, 72 | parallelism, 73 | Some(32), 74 | )?, 75 | ); 76 | 77 | let mut hash = [0u8; 32]; 78 | argon.hash_password_into(password, salt, &mut hash)?; 79 | Ok(hash.to_vec()) 80 | } 81 | -------------------------------------------------------------------------------- /crates/bitwarden-wasm/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod client; 2 | -------------------------------------------------------------------------------- /crates/bitwarden/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project 6 | adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ## [1.0.0] - 2024-09-26 11 | 12 | ### Added 13 | 14 | - Support for secrets sync (#678) 15 | - Password generator (#986) 16 | 17 | ### Changed 18 | 19 | - `ClientSettings` and `DeviceType` is now exported in the root module (#805) 20 | - Secrets Manager now requires `bitwarden::secrets_manager::ClientSecretsExt` and 21 | `bitwarden::secrets_manager::ClientProjectsExt` to be imported in order to access `secrets()` and 22 | `projects` on the client (#798) 23 | - Updated MSRV `1.75.0` (#980) 24 | 25 | ### Removed 26 | 27 | - The deprecated `client.access_token_login()` is now removed. Please use 28 | `client.auth().login_access_token()` instead. (#806) 29 | 30 | ## [0.5.0] - 2024-04-26 31 | 32 | ### Changed 33 | 34 | - Switched TLS backend to `rustls`, removing the dependency on `OpenSSL`. (#374) 35 | - `client::AccessToken` is now `auth::AccessToken`. (#656) 36 | 37 | ### Fixed 38 | 39 | - Fix renew for service account access token logins (#702) 40 | 41 | ## [0.4.0] - 2023-12-21 42 | 43 | ### Added 44 | 45 | - Support for basic state to avoid reauthenticating when creating a new `Client`. This is a breaking 46 | change because of adding `state_file` to the `AccessTokenLoginRequest` struct. (#388) 47 | 48 | ### Deprecated 49 | 50 | - `client.access_token_login()` is now deprecated and will be removed in a future release. Please 51 | use `client.auth().login_access_token()` instead. (#319) 52 | 53 | ## [0.3.1] - 2023-10-13 54 | 55 | ### Changed 56 | 57 | - `auth::request::AccessTokenLoginRequest` moved to `auth::login::AccessTokenLoginRequest` (#178) 58 | - Support for fetching multiple secrets by ids (#150) 59 | 60 | ## [0.3.0] - 2023-07-26 61 | 62 | ### Deprecated 63 | 64 | - The secrets manager SDK is now hidden behind a `secrets` feature flag. Make sure to enable this 65 | flag in your `Cargo.toml` file. At the moment the flag is enabled by default for compatibility 66 | reasons, but this is considered deprecated and the flag will be made opt-in eventually. 67 | 68 | ### Added 69 | 70 | - Support for creating and editing secrets (#77) 71 | - Support for creating and editing projects (#53) 72 | 73 | ### Changed 74 | 75 | - Folder structure, update `use` declarations (#68) 76 | 77 | ### Fixed 78 | 79 | - Improve login error handling (#109) 80 | 81 | ## [0.2.1] - 2023-03-22 82 | 83 | ### Fixed 84 | 85 | - Add user agent to login requests (#11) 86 | -------------------------------------------------------------------------------- /crates/bitwarden/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bitwarden" 3 | description = """ 4 | Bitwarden Secrets Manager SDK 5 | """ 6 | keywords = ["bitwarden", "secrets-manager"] 7 | 8 | version.workspace = true 9 | authors.workspace = true 10 | edition.workspace = true 11 | rust-version.workspace = true 12 | homepage.workspace = true 13 | repository.workspace = true 14 | license-file.workspace = true 15 | 16 | [features] 17 | default = ["secrets"] 18 | 19 | no-memory-hardening = [ 20 | "bitwarden-core/no-memory-hardening", 21 | ] # Disable memory hardening features 22 | secrets = [ 23 | "bitwarden-core/secrets", 24 | "dep:bitwarden-sm", 25 | "dep:bitwarden-generators", 26 | ] # Secrets manager API 27 | wasm = [] # WASM support 28 | 29 | [dependencies] 30 | bitwarden-core = { workspace = true } 31 | bitwarden-generators = { workspace = true, optional = true } 32 | bitwarden-sm = { workspace = true, optional = true } 33 | thiserror = ">=1.0.40, <2.0" 34 | 35 | [dev-dependencies] 36 | uuid = { version = ">=1.3.3, <2.0", features = ["serde", "v4"] } 37 | 38 | [lints] 39 | workspace = true 40 | -------------------------------------------------------------------------------- /crates/bitwarden/README.md: -------------------------------------------------------------------------------- 1 | # Bitwarden Secrets Manager SDK 2 | 3 | A Rust client SDK to interact with the 4 | [Bitwarden Secrets Manager](https://bitwarden.com/products/secrets-manager/). This is a beta release 5 | and might be missing some functionality. 6 | 7 | ## Usage 8 | 9 | ```toml 10 | [dependencies] 11 | bitwarden = { "*", features = ["secrets"] } 12 | ``` 13 | 14 | ## Minimum Supported Rust Version 15 | 16 | Rust **1.75** or higher. 17 | 18 | ## Example 19 | 20 | ```rust 21 | use bitwarden::{ 22 | auth::login::AccessTokenLoginRequest, 23 | error::Result, 24 | secrets_manager::{secrets::SecretIdentifiersRequest, ClientSecretsExt}, 25 | Client, ClientSettings, DeviceType, 26 | }; 27 | use uuid::Uuid; 28 | 29 | async fn test() -> Result<()> { 30 | // Use the default values 31 | let mut client = Client::new(None); 32 | 33 | // Or set your own values 34 | let settings = ClientSettings { 35 | identity_url: "https://identity.bitwarden.com".to_string(), 36 | api_url: "https://api.bitwarden.com".to_string(), 37 | user_agent: "Bitwarden Rust-SDK".to_string(), 38 | device_type: DeviceType::SDK, 39 | }; 40 | let mut client = Client::new(Some(settings)); 41 | 42 | // Before we operate, we need to authenticate with a token 43 | let token = AccessTokenLoginRequest { 44 | access_token: String::from(""), 45 | state_file: None, 46 | }; 47 | client.auth().login_access_token(&token).await.unwrap(); 48 | 49 | let org_id = SecretIdentifiersRequest { 50 | organization_id: Uuid::parse_str("00000000-0000-0000-0000-000000000000").unwrap(), 51 | }; 52 | println!( 53 | "Stored secrets: {:#?}", 54 | client.secrets().list(&org_id).await.unwrap() 55 | ); 56 | Ok(()) 57 | } 58 | ``` 59 | -------------------------------------------------------------------------------- /crates/bitwarden/src/error.rs: -------------------------------------------------------------------------------- 1 | //! Errors that can occur when using this SDK 2 | 3 | use std::fmt::Debug; 4 | 5 | use thiserror::Error; 6 | 7 | #[derive(Debug, Error)] 8 | pub enum Error { 9 | #[error(transparent)] 10 | Core(#[from] bitwarden_core::Error), 11 | } 12 | 13 | // Ensure that the error messages implement Send and Sync 14 | #[cfg(test)] 15 | const _: () = { 16 | fn assert_send() {} 17 | fn assert_sync() {} 18 | fn assert_all() { 19 | assert_send::(); 20 | assert_sync::(); 21 | } 22 | }; 23 | 24 | pub type Result = std::result::Result; 25 | -------------------------------------------------------------------------------- /crates/bitwarden/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Bitwarden 2 | //! 3 | //! A Rust client SDK to interact with the Bitwarden Secrets Manager. 4 | //! This is a beta release and might be missing some functionality. 5 | //! 6 | //! To use this crate, add it to your `Cargo.toml`: 7 | //! 8 | //! ```ini 9 | //! [dependencies] 10 | //! bitwarden = { "*", features = ["secrets"] } 11 | //! ``` 12 | //! 13 | //! # Basic setup 14 | //! 15 | //! All operations in this crate are done via a [Client]: 16 | //! 17 | //! ```rust 18 | //! use bitwarden::{ 19 | //! auth::login::AccessTokenLoginRequest, 20 | //! error::Result, 21 | //! secrets_manager::{secrets::SecretIdentifiersRequest, ClientSecretsExt}, 22 | //! Client, ClientSettings, DeviceType, 23 | //! }; 24 | //! use uuid::Uuid; 25 | //! 26 | //! async fn test() -> Result<()> { 27 | //! // Use the default values 28 | //! let mut client = Client::new(None); 29 | //! 30 | //! // Or set your own values 31 | //! let settings = ClientSettings { 32 | //! identity_url: "https://identity.bitwarden.com".to_string(), 33 | //! api_url: "https://api.bitwarden.com".to_string(), 34 | //! user_agent: "Bitwarden Rust-SDK".to_string(), 35 | //! device_type: DeviceType::SDK, 36 | //! }; 37 | //! let mut client = Client::new(Some(settings)); 38 | //! 39 | //! // Before we operate, we need to authenticate with a token 40 | //! let token = AccessTokenLoginRequest { 41 | //! access_token: String::from(""), 42 | //! state_file: None, 43 | //! }; 44 | //! client.auth().login_access_token(&token).await.unwrap(); 45 | //! 46 | //! let org_id = SecretIdentifiersRequest { 47 | //! organization_id: Uuid::parse_str("00000000-0000-0000-0000-000000000000").unwrap(), 48 | //! }; 49 | //! println!( 50 | //! "Stored secrets: {:#?}", 51 | //! client.secrets().list(&org_id).await.unwrap() 52 | //! ); 53 | //! Ok(()) 54 | //! } 55 | //! ``` 56 | 57 | // Ensure the readme docs compile 58 | #[doc = include_str!("../README.md")] 59 | mod readme {} 60 | 61 | pub use bitwarden_core::*; 62 | pub mod error; 63 | 64 | #[cfg(feature = "secrets")] 65 | pub mod generators { 66 | pub use bitwarden_generators::{ClientGeneratorExt, PasswordError, PasswordGeneratorRequest}; 67 | } 68 | 69 | #[cfg(feature = "secrets")] 70 | pub mod secrets_manager { 71 | pub use bitwarden_sm::*; 72 | } 73 | -------------------------------------------------------------------------------- /crates/bws/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project 6 | adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ## [1.0.0] - 2024-09-26 11 | 12 | ### Added 13 | 14 | - The ability to edit unassigned secrets with direct permissions. (#906) 15 | - The `run` command, to run commands with secrets (#621) 16 | 17 | ### Changed 18 | 19 | - Updated MSRV `1.75.0` (#980) 20 | - Use state files by default. You can opt out of this behavior with the new `state_opt_out` key. 21 | (#930) 22 | - Opt out documentation can be found 23 | [here](https://bitwarden.com/help/secrets-manager-cli/#config-state) 24 | 25 | ### Removed 26 | 27 | - The deprecated `action type` commands are now removed. Please use `type action` instead. (#836) 28 | 29 | ## [0.5.0] - 2024-04-26 30 | 31 | ### Added 32 | 33 | - Add a `BWS_CONFIG_FILE` environment variable to specify the location of the config file (#571) 34 | - The `bws` CLI is now available as a Docker image (`docker run -it bitwarden/bws --help`) (#305) 35 | - The `bws` CLI releases are now code signed on Windows and Mac (#534, #535) 36 | 37 | ### Fixed 38 | 39 | - Re-add output options to the help menu after they were accidentally removed (#477) 40 | 41 | ### Changed 42 | 43 | - Switched TLS backend to `rusttls`, removing the dependency on `OpenSSL` (#374) 44 | - Updated MSRV for `bws` to `1.71.0` (#589) 45 | 46 | ## [0.4.0] - 2023-12-21 47 | 48 | ### Added 49 | 50 | - Ability to output secrets in an `env` format with `bws` (#320) 51 | - Basic state to avoid reauthenticating every run, used when setting the `state_file_dir` key in the 52 | config (#388) 53 | 54 | ## [0.3.1] - 2023-10-13 55 | 56 | ### Added 57 | 58 | - Support for shell autocompletion with the `bws completions` command (#103) 59 | - When running `bws` with no args, the help text is now printed to `stderr` instead of `stdout` to 60 | be consistent with `bws subcommand` behavior (#190) 61 | 62 | ## [0.3.0] - 2023-07-26 63 | 64 | ### Deprecated 65 | 66 | - Switched command order from `action type` to `type action`, please re-read the help documentation 67 | (#76) 68 | 69 | ### Added 70 | 71 | - Ability to create and edit projects (#53) 72 | - Ability to create and edit secrets (#77) 73 | - Support `NO_COLOR` environment variable to disable CLI colors (#61) 74 | - Support for `CLICOLOR_FORCE` (#74) 75 | 76 | ### Fixed 77 | 78 | - Improve login error handling (#109) 79 | - Respect users color choice for errors (#61) 80 | 81 | ## [0.2.1] - 2023-03-22 82 | 83 | ### Fixed 84 | 85 | - Add user agent to login requests (#11) 86 | -------------------------------------------------------------------------------- /crates/bws/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bws" 3 | version = "1.0.0" 4 | description = """ 5 | Bitwarden Secrets Manager CLI 6 | """ 7 | keywords = ["bitwarden", "secrets-manager", "cli"] 8 | exclude = ["Dockerfile*", "entitlements.plist"] 9 | 10 | authors.workspace = true 11 | edition.workspace = true 12 | rust-version.workspace = true 13 | homepage.workspace = true 14 | repository.workspace = true 15 | license-file.workspace = true 16 | 17 | [dependencies] 18 | bat = { version = "0.24.0", features = [ 19 | "regex-onig", 20 | ], default-features = false } 21 | bitwarden = { workspace = true, features = ["secrets"] } 22 | bitwarden-cli = { workspace = true } 23 | chrono = { version = "0.4.38", features = [ 24 | "clock", 25 | "std", 26 | ], default-features = false } 27 | clap = { version = "4.5.4", features = ["derive", "env", "string"] } 28 | clap_complete = "4.5.2" 29 | color-eyre = "0.6.3" 30 | comfy-table = "7.1.1" 31 | directories = "5.0.1" 32 | env_logger = "0.11.1" 33 | itertools = "0.13.0" 34 | log = "0.4.20" 35 | regex = { version = "1.10.3", features = [ 36 | "std", 37 | "perf", 38 | ], default-features = false } 39 | serde = "1.0.196" 40 | serde_json = "1.0.113" 41 | serde_yaml = "0.9" 42 | supports-color = "3.0.0" 43 | thiserror = "1.0.57" 44 | tokio = { workspace = true, features = ["rt-multi-thread"] } 45 | toml = "0.8.10" 46 | uuid = { version = "1.7.0", features = ["serde"] } 47 | which = "6.0.1" 48 | 49 | [build-dependencies] 50 | bitwarden-cli = { workspace = true } 51 | clap = { version = "4.5.4", features = ["derive", "string"] } 52 | clap_complete = "4.5.2" 53 | clap_mangen = "0.2.20" 54 | uuid = { version = "1.7.0" } 55 | 56 | [dev-dependencies] 57 | tempfile = "3.10.0" 58 | 59 | [lints] 60 | workspace = true 61 | -------------------------------------------------------------------------------- /crates/bws/Dockerfile: -------------------------------------------------------------------------------- 1 | ############################################### 2 | # Build stage # 3 | ############################################### 4 | FROM --platform=$BUILDPLATFORM rust:1.81 AS build 5 | 6 | # Docker buildx supplies the value for this arg 7 | ARG TARGETPLATFORM 8 | 9 | RUN apt-get update && apt-get install -y --no-install-recommends \ 10 | ca-certificates \ 11 | && rm -rf /var/lib/apt/lists/* 12 | 13 | # Copy required project files 14 | COPY . /app 15 | 16 | # Build project 17 | WORKDIR /app/crates/bws 18 | RUN cargo build --release --bin bws 19 | 20 | # Bundle bws dependencies 21 | RUN mkdir /lib-bws 22 | RUN mkdir /lib64-bws 23 | 24 | RUN ldd /app/target/release/bws | tr -s '[:blank:]' '\n' | grep '^/lib' | xargs -I % cp % /lib-bws 25 | RUN ldd /app/target/release/bws | tr -s '[:blank:]' '\n' | grep '^/lib64' | xargs -I % cp % /lib64-bws 26 | 27 | # Make a user and HOME directory for the app stage 28 | RUN useradd -m app 29 | 30 | ############################################### 31 | # App stage # 32 | ############################################### 33 | FROM scratch 34 | 35 | ARG TARGETPLATFORM 36 | LABEL com.bitwarden.product="bitwarden" 37 | 38 | # Set a HOME directory and copy the user file 39 | COPY --from=build /home/app /home/app 40 | COPY --from=build /etc/passwd /etc/passwd 41 | ENV HOME=/home/app 42 | WORKDIR /home/app 43 | 44 | # Switch to the app user 45 | USER app 46 | 47 | # Copy built project from the build stage 48 | COPY --from=build /app/target/release/bws /bin/bws 49 | 50 | # Copy certs 51 | COPY --from=build /etc/ssl/certs /etc/ssl/certs 52 | 53 | # Copy bws dependencies 54 | COPY --from=build /lib-bws /lib 55 | COPY --from=build /lib64-bws /lib64 56 | 57 | ENTRYPOINT ["bws"] 58 | -------------------------------------------------------------------------------- /crates/bws/Dockerfile.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !crates/* 3 | !Cargo.toml 4 | !Cargo.lock 5 | -------------------------------------------------------------------------------- /crates/bws/README.md: -------------------------------------------------------------------------------- 1 | # Bitwarden Secrets Manager CLI 2 | 3 | A Rust CLI for interacting with the 4 | [Bitwarden Secrets Manager](https://bitwarden.com/products/secrets-manager/). This is a beta release 5 | and might be missing some functionality. 6 | 7 | ## Install 8 | 9 | We offer three ways to install bws: 10 | 11 | ### Cargo (crates.io) 12 | 13 | Download bws via `cargo` from [crates.io](https://crates.io): 14 | 15 | ```bash 16 | cargo install bws --locked 17 | ``` 18 | 19 | ### Install Script (from GitHub Releases) 20 | 21 | Linux/macOS: `curl https://bws.bitwarden.com/install | sh` 22 | 23 | Windows: `iwr https://bws.bitwarden.com/install | iex` 24 | 25 | An optional `-u/--uninstall` flag can be passed to the POSIX script to uninstall the CLI. The 26 | PowerShell version accepts an equivalent `-Uninstall` flag. The uninstallation process will remove 27 | the `bws` binary and the configuration directory (`~/.bws`). 28 | 29 | ### GitHub Releases (Manual) 30 | 31 | Download a pre-built binary from the [Releases](https://github.com/bitwarden/sdk-sm/releases) page. 32 | 33 | ## Usage 34 | 35 | ```bash 36 | bws --help 37 | ``` 38 | 39 | ## How to enable shell autocompletions 40 | 41 | ### Zsh 42 | 43 | If completion is not enabled already, you need to enable it first: 44 | 45 | ```zsh 46 | echo "autoload -U compinit; compinit" >> ~/.zshrc 47 | ``` 48 | 49 | Enable autocompletions for the current user: 50 | 51 | ```zsh 52 | echo 'source <(/path/to/bws completions zsh)' >> ~/.zshrc 53 | ``` 54 | 55 | ### Bash 56 | 57 | Enable autocompletions for the current user: 58 | 59 | ```zsh 60 | echo 'source <(/path/to/bws completions bash)' >> ~/.bashrc 61 | ``` 62 | 63 | For more detailed documentation, please refer to the 64 | [Secrets Manager CLI help article](https://bitwarden.com/help/secrets-manager-cli/). 65 | 66 | ## Docker 67 | 68 | We also provide a docker image preloaded with the `bws` cli. 69 | 70 | ```bash 71 | # From the root of the repository 72 | docker build -f crates/bws/Dockerfile -t bitwarden/bws . 73 | 74 | docker run --rm -it bitwarden/bws --help 75 | ``` 76 | 77 | To use a configuration file, utilize docker 78 | [bind mounting](https://docs.docker.com/storage/bind-mounts/) to expose it to the container: 79 | 80 | ```bash 81 | docker run --rm -it -v "$HOME"/.bws:/home/app/.bws bitwarden/bws --help 82 | ``` 83 | 84 | ## How to build manpages 85 | 86 | The manpages get built during compilation of the `bws` crate through the use of a build script. The 87 | output path of this build script can be located as follows: 88 | 89 | ``` 90 | MANPAGES_DIR=$(cargo build -p bws --message-format json | jq -r --slurp '.[] | select (.reason == "build-script-executed") | select(.package_id|contains("crates/bws")) .out_dir') 91 | ``` 92 | 93 | After running the provided commands, the built manpages should be located in 94 | `$MANPAGES_DIR/manpages` 95 | -------------------------------------------------------------------------------- /crates/bws/build.rs: -------------------------------------------------------------------------------- 1 | include!("src/cli.rs"); 2 | 3 | fn main() -> Result<(), std::io::Error> { 4 | use std::{env, fs, path::Path}; 5 | 6 | let out_dir = env::var_os("OUT_DIR").expect("OUT_DIR exists"); 7 | let path = Path::new(&out_dir).join("manpages"); 8 | fs::create_dir_all(&path).expect("OUT_DIR is writable"); 9 | 10 | let cmd = ::command(); 11 | clap_mangen::generate_to(cmd, &path)?; 12 | 13 | Ok(()) 14 | } 15 | -------------------------------------------------------------------------------- /crates/bws/entitlements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.allow-unsigned-executable-memory 6 | 7 | 8 | -------------------------------------------------------------------------------- /crates/bws/src/command/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod project; 2 | pub(crate) mod run; 3 | pub(crate) mod secret; 4 | 5 | use std::{path::PathBuf, str::FromStr}; 6 | 7 | use bitwarden::auth::AccessToken; 8 | use clap::CommandFactory; 9 | use clap_complete::Shell; 10 | use color_eyre::eyre::{bail, Result}; 11 | 12 | use crate::{config, util, Cli, ProfileKey}; 13 | 14 | pub(crate) fn completions(shell: Option) -> Result<()> { 15 | let Some(shell) = shell.or_else(Shell::from_env) else { 16 | bail!("Couldn't autodetect a valid shell. Run `bws completions --help` for more info."); 17 | }; 18 | 19 | let mut cmd = Cli::command(); 20 | let name = cmd.get_name().to_string(); 21 | clap_complete::generate(shell, &mut cmd, name, &mut std::io::stdout()); 22 | 23 | Ok(()) 24 | } 25 | 26 | pub(crate) fn config( 27 | name: Option, 28 | value: Option, 29 | delete: bool, 30 | profile: Option, 31 | access_token: Option, 32 | config_file: Option, 33 | ) -> Result<()> { 34 | let profile = if let Some(profile) = profile { 35 | profile 36 | } else if let Some(access_token) = access_token { 37 | AccessToken::from_str(&access_token)? 38 | .access_token_id 39 | .to_string() 40 | } else { 41 | String::from("default") 42 | }; 43 | 44 | if delete { 45 | config::delete_profile(config_file.as_deref(), profile)?; 46 | println!("Profile deleted successfully!"); 47 | } else { 48 | let (name, value) = match (name, value) { 49 | (None, None) => bail!("Missing `name` and `value`"), 50 | (None, Some(_)) => bail!("Missing `value`"), 51 | (Some(_), None) => bail!("Missing `name`"), 52 | (Some(ProfileKey::state_opt_out), Some(value)) => { 53 | if util::string_to_bool(value.as_str()).is_err() { 54 | bail!("Profile key \"state_opt_out\" must be \"true\" or \"false\""); 55 | } else { 56 | (ProfileKey::state_opt_out, value) 57 | } 58 | } 59 | (Some(name), Some(value)) => (name, value), 60 | }; 61 | 62 | config::update_profile(config_file.as_deref(), profile, name, value)?; 63 | println!("Profile updated successfully!"); 64 | }; 65 | 66 | Ok(()) 67 | } 68 | -------------------------------------------------------------------------------- /crates/bws/src/state.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use color_eyre::eyre::{bail, Result}; 4 | use directories::BaseDirs; 5 | 6 | use crate::DEFAULT_CONFIG_DIRECTORY; 7 | 8 | pub(crate) const DEFAULT_STATE_DIRECTORY: &str = "state"; 9 | 10 | pub(crate) fn get_state_file( 11 | state_dir: Option, 12 | access_token_id: String, 13 | ) -> Result { 14 | let mut state_dir = match state_dir { 15 | Some(state_dir) => state_dir, 16 | None => { 17 | if let Some(base_dirs) = BaseDirs::new() { 18 | base_dirs 19 | .home_dir() 20 | .join(DEFAULT_CONFIG_DIRECTORY) 21 | .join(DEFAULT_STATE_DIRECTORY) 22 | } else { 23 | bail!("A valid home directory doesn't exist"); 24 | } 25 | } 26 | }; 27 | 28 | std::fs::create_dir_all(&state_dir)?; 29 | state_dir.push(access_token_id); 30 | 31 | Ok(state_dir) 32 | } 33 | -------------------------------------------------------------------------------- /crates/sdk-schemas/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sdk-schemas" 3 | version = "0.1.0" 4 | publish = false 5 | 6 | authors.workspace = true 7 | edition.workspace = true 8 | rust-version.workspace = true 9 | homepage.workspace = true 10 | repository.workspace = true 11 | license-file.workspace = true 12 | keywords.workspace = true 13 | 14 | [features] 15 | 16 | [dependencies] 17 | anyhow = "1.0.82" 18 | bitwarden = { workspace = true } 19 | bitwarden-json = { path = "../bitwarden-json" } 20 | itertools = "0.13.0" 21 | schemars = { workspace = true, features = ["preserve_order"] } 22 | serde_json = "1.0.113" 23 | -------------------------------------------------------------------------------- /languages/cpp/CMakeBuild.md: -------------------------------------------------------------------------------- 1 | # CMake Build 2 | 3 | ## Introduction 4 | 5 | Cmake is used to build the C++ Bitwarden client library. Output should be placed in the build directory. 6 | The output contains two dynamic libraries: 7 | 8 | - The C++ client `BitwardenClient` 9 | - The Bitwarden library used by the C++ client `bitwarden_c`. 10 | 11 | See how to use these libraries in the [example use guide](./examples/ExampleUse.md) 12 | 13 | ## Prerequisites 14 | 15 | - Cmake installed, minimum version 3.15 16 | - `schemas.hpp` generated into `include` directory 17 | - installed `nlohmann-json` library 18 | - installed `boost` library 19 | 20 | ## Build Commands 21 | 22 | One should be in the root directory of the C++ wrapper (the same level where is CMakeLists.txt placed). Paths of the 23 | three libraries should be placed inside the cmake build command: 24 | 25 | ```bash 26 | mkdir -p build 27 | cd build 28 | cmake .. -DNLOHMANN=/path/to/include/nlohmann -DBOOST=/path/to/include/boost -DTARGET=relative/path/to/libbitwarden_c 29 | cmake --build . 30 | ``` 31 | 32 | ## IDE Support 33 | 34 | You may need to manually set the CMake `TARGET` variable for your IDE. For CLion, add the following to the CMake options 35 | settings: 36 | 37 | ```bash 38 | # macOS example 39 | -DTARGET=../../target/release/libbitwarden_c.dylib 40 | ``` 41 | 42 | ## Example 43 | 44 | ### macOS 45 | 46 | #### Install Prerequisites 47 | 48 | ```bash 49 | brew install cmake 50 | brew install boost 51 | brew install nlohmann-json 52 | ``` 53 | 54 | #### Build 55 | 56 | ```bash 57 | mkdir -p build 58 | cd build 59 | cmake .. -DNLOHMANN=/opt/homebrew/include -DBOOST=/opt/homebrew/include -DTARGET=../../target/release/libbitwarden_c.dylib 60 | cmake --build . 61 | ``` 62 | -------------------------------------------------------------------------------- /languages/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | project(BitwardenClient) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) 6 | 7 | # Set placeholders to be passed from command line 8 | set(NLOHMANN_JSON_INCLUDE_DIR_PLACEHOLDER ${NLOHMANN}) 9 | set(BOOST_INCLUDE_DIR_PLACEHOLDER ${BOOST}) 10 | set(TARGET_INCLUDE_DIR_PLACEHOLDER ${TARGET}) 11 | 12 | # Specify the locations of nlohmann.json and Boost libraries 13 | find_path(NLOHMANN_JSON_INCLUDE_DIR nlohmann/json.hpp HINTS ${NLOHMANN_JSON_INCLUDE_DIR_PLACEHOLDER}) 14 | find_path(BOOST_INCLUDE_DIR boost/optional.hpp HINTS ${BOOST_INCLUDE_DIR_PLACEHOLDER}) 15 | 16 | # Include directories for library 17 | include_directories(include ${NLOHMANN_JSON_INCLUDE_DIR} ${BOOST_INCLUDE_DIR}) 18 | 19 | # Add library source files 20 | file(GLOB SOURCES "src/*.cpp") 21 | 22 | # Add library source files along with the schemas.cpp file 23 | add_library(BitwardenClient SHARED ${SOURCES} ${SCHEMAS_SOURCE}) 24 | 25 | # Set path for native library loading 26 | set(LIB_BITWARDEN_C "${CMAKE_SOURCE_DIR}/${TARGET}") 27 | 28 | # Copy the library to the build directory before building 29 | add_custom_command( 30 | TARGET BitwardenClient PRE_BUILD 31 | COMMAND ${CMAKE_COMMAND} -E copy 32 | ${LIB_BITWARDEN_C} 33 | $ 34 | ) 35 | 36 | # Link libraries 37 | target_link_libraries(BitwardenClient PRIVATE ${LIB_BITWARDEN_C}) 38 | -------------------------------------------------------------------------------- /languages/cpp/examples/ExampleUse.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | ## Prerequisites 4 | 5 | ### Bitwarden Libraries 6 | 7 | Have the two Bitwarden libraries at the same path: 8 | 9 | - `BitwardenClient` 10 | - `bitwarden_c` 11 | 12 | For each OS the library files will be the following: 13 | 14 | - macOS: `libBitwardenClient.dylib` and `libbitwarden_c.dylib` 15 | - Linux: `libBitwardenClient.so` and `libbitwarden_c.so` 16 | - Windows: `BitwardenClient.dll` and `bitwarden_c.dll` 17 | 18 | Follow the [cmake build guide](../CMakeBuild.md) to create the libraries locally. 19 | 20 | ### Include Directory 21 | 22 | `include` directory contains: 23 | 24 | - `BitwardenLibrary.h` 25 | - `BitwardenClient.h` 26 | - `BitwardenSettings.h` 27 | - `CommandRunner.h` 28 | - `Projects.h` 29 | - `Secrets.h` 30 | - `schemas.hpp` 31 | 32 | ### Other Libraries 33 | 34 | - `nlohmann-json` () 35 | - `boost` () 36 | 37 | ### Compiling 38 | 39 | Use g++/clang++ for compiling. 40 | 41 | Example of the folder structure (macOS): 42 | 43 | ```text 44 | --root 45 | --build 46 | `libBitwardenClient.dylib` 47 | `libbitwarden_c.dylib` 48 | --include 49 | --`BitwardenLibrary.h` 50 | --`BitwardenClient.h` 51 | --`BitwardenSettings.h` 52 | --`CommandRunner.h` 53 | --`Projects.h` 54 | --`Secrets.h` 55 | --`schemas.hpp` 56 | --examples 57 | --`Wrapper.cpp` 58 | ``` 59 | 60 | Set the environment variable path for the Bitwarden libraries. 61 | 62 | For macOS: 63 | 64 | ```bash 65 | export DYLD_LIBRARY_PATH=/path/to/your/library:$DYLD_LIBRARY_PATH 66 | ``` 67 | 68 | For Linux: 69 | 70 | ```bash 71 | export LD_LIBRARY_PATH=/path/to/your/library:$LD_LIBRARY_PATH 72 | ``` 73 | 74 | For Windows: 75 | 76 | ```shell 77 | set "PATH=%PATH%;C:\path\to\your\library" 78 | ``` 79 | 80 | Set environment variables used in `Wrapper.cpp`: 81 | 82 | ```bash 83 | export ACCESS_TOKEN=<"access-token"> 84 | export ORGANIZATION_ID=<"organization-id"> 85 | export API_URL=http://localhost:4000 86 | export IDENTITY_URL=http://localhost:33656 87 | ``` 88 | 89 | Compile: 90 | 91 | ```bash 92 | cd examples 93 | clang++ -std=c++20 -I../include -I/path/to/include/nlohmann -I/path/to/include/boost -L../build/ -o MyBitwardenApp Wrapper.cpp -lBitwardenClient -ldl 94 | ``` 95 | 96 | for Windows `-ldl` should be excluded, 97 | 98 | for macOS nlohmann and boost libraries installed with homebrew the following can be used: 99 | 100 | ```bash 101 | -I/opt/homebrew/include 102 | ``` 103 | 104 | The result is `MyBitwardenApp` in the `examples` directory, and can be ran from the `examples` directory: 105 | 106 | ```bash 107 | ./MyBitwardenApp 108 | ``` 109 | -------------------------------------------------------------------------------- /languages/cpp/include/BitwardenClient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CommandRunner.h" 4 | #include "BitwardenSettings.h" 5 | #include "Projects.h" 6 | #include "Secrets.h" 7 | #include 8 | #include 9 | 10 | class BitwardenClient { 11 | public: 12 | explicit BitwardenClient(const BitwardenSettings& bitwardenSettings = BitwardenSettings()); 13 | ~BitwardenClient(); 14 | 15 | void loginAccessToken(const std::string& accessToken, const std::string& stateFile = ""); 16 | ProjectResponse getProject(const boost::uuids::uuid& id); 17 | ProjectResponse createProject(const boost::uuids::uuid& organizationId, const std::string& name); 18 | ProjectResponse updateProject(const boost::uuids::uuid& organizationId, const boost::uuids::uuid& id, const std::string& name); 19 | ProjectsDeleteResponse deleteProjects(const std::vector& ids); 20 | ProjectsResponse listProjects(const boost::uuids::uuid &organizationId); 21 | SecretResponse getSecret(const boost::uuids::uuid& id); 22 | SecretsResponse getSecretsByIds(const std::vector& ids); 23 | SecretResponse createSecret(const boost::uuids::uuid& organizationId, const std::string& key, const std::string& value, const std::string& note, const std::vector& projectIds); 24 | SecretResponse updateSecret(const boost::uuids::uuid& organizationId, const boost::uuids::uuid& id, const std::string& key, const std::string& value, const std::string& note, const std::vector& projectIds); 25 | SecretsDeleteResponse deleteSecrets(const std::vector& ids); 26 | SecretIdentifiersResponse listSecrets(const boost::uuids::uuid& organizationId); 27 | SecretsSyncResponse sync(const boost::uuids::uuid &organizationId, const std::chrono::system_clock::time_point &lastSyncedDate); 28 | 29 | private: 30 | BitwardenLibrary* library; 31 | void* client; 32 | CommandRunner* commandRunner; 33 | Projects projects; 34 | Secrets secrets; 35 | bool isClientOpen; 36 | ClientSettings clientSettings; 37 | 38 | }; 39 | -------------------------------------------------------------------------------- /languages/cpp/include/BitwardenLibrary.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef _WIN32 6 | #include 7 | #else 8 | #include 9 | #endif 10 | 11 | class BitwardenLibrary { 12 | public: 13 | BitwardenLibrary(const std::string& providedLibraryPath); 14 | ~BitwardenLibrary(); 15 | 16 | void* init(const char* clientSettingsJson); 17 | void free_mem(void* client); 18 | const char* run_command(const char* commandJson, void* client); 19 | 20 | private: 21 | #ifdef _WIN32 22 | HMODULE libraryHandle; 23 | #else 24 | void* libraryHandle; 25 | #endif 26 | }; 27 | 28 | -------------------------------------------------------------------------------- /languages/cpp/include/BitwardenSettings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class BitwardenSettings { 6 | public: 7 | BitwardenSettings() = default; 8 | ~BitwardenSettings() = default; 9 | 10 | const std::string& get_api_url() const { return api_url; } 11 | void set_api_url(const std::string& value) { api_url = value; } 12 | 13 | const std::string& get_identity_url() const { return identity_url; } 14 | void set_identity_url(const std::string& value) { identity_url = value; } 15 | 16 | private: 17 | std::string api_url; 18 | std::string identity_url; 19 | }; 20 | -------------------------------------------------------------------------------- /languages/cpp/include/CommandRunner.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "BitwardenLibrary.h" 6 | #include "schemas.hpp" 7 | #include 8 | 9 | using namespace Bitwarden::Sdk; 10 | 11 | class CommandRunner { 12 | public: 13 | CommandRunner(BitwardenLibrary* library, void* client); 14 | 15 | template 16 | R runCommand(const Command& command, Func deserializer); 17 | 18 | 19 | 20 | private: 21 | BitwardenLibrary* library; 22 | void* client; 23 | 24 | std::string commandToString(const Command& command); 25 | nlohmann::json filterNullObjects(const nlohmann::json& input); 26 | }; 27 | 28 | template 29 | R CommandRunner::runCommand(const Command& command, Func deserializer) { 30 | // Serialize the Command object to a JSON string 31 | std::string jsonString = commandToString(command); 32 | const char* jsonCStr = jsonString.c_str(); 33 | const char* response = library->run_command(jsonCStr, client); 34 | 35 | // Deserialize the response using the provided deserializer function 36 | T deserialized = deserializer(response); 37 | 38 | // Unwrap the response and throw an exception if it was not successful 39 | if (!deserialized.get_success()) { 40 | throw std::runtime_error(*deserialized.get_error_message()); 41 | } 42 | 43 | return deserialized.get_data().get(); 44 | } 45 | 46 | -------------------------------------------------------------------------------- /languages/cpp/include/Projects.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "CommandRunner.h" 6 | 7 | class Projects { 8 | public: 9 | Projects(CommandRunner* commandRunner); 10 | 11 | ProjectResponse get(const boost::uuids::uuid& id); 12 | ProjectResponse create(const boost::uuids::uuid& organizationId, const std::string& name); 13 | ProjectResponse update(const boost::uuids::uuid& organizationId, const boost::uuids::uuid& id, const std::string& name); 14 | ProjectsDeleteResponse deleteProjects(const std::vector& ids); 15 | ProjectsResponse list(const boost::uuids::uuid& organizationId); 16 | 17 | private: 18 | CommandRunner* commandRunner; 19 | }; 20 | -------------------------------------------------------------------------------- /languages/cpp/include/Secrets.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "CommandRunner.h" 8 | 9 | class Secrets { 10 | public: 11 | Secrets(CommandRunner* commandRunner); 12 | 13 | SecretResponse get(const boost::uuids::uuid& id); 14 | SecretsResponse getByIds(const std::vector &ids); 15 | SecretResponse create(const boost::uuids::uuid& organizationId, const std::string& key, const std::string& value, const std::string& note, const std::vector& projectIds); 16 | SecretResponse update(const boost::uuids::uuid& organizationId, const boost::uuids::uuid& id, const std::string& key, const std::string& value, const std::string& note, const std::vector& projectIds); 17 | SecretsDeleteResponse deleteSecrets(const std::vector& ids); 18 | SecretIdentifiersResponse list(const boost::uuids::uuid& organizationId); 19 | SecretsSyncResponse sync(const boost::uuids::uuid& organizationId, const boost::optional& lastSyncedDate); 20 | 21 | private: 22 | CommandRunner* commandRunner; 23 | }; 24 | 25 | -------------------------------------------------------------------------------- /languages/cpp/src/CommandRunner.cpp: -------------------------------------------------------------------------------- 1 | #include "CommandRunner.h" 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | CommandRunner::CommandRunner(BitwardenLibrary* library, void* client) : library(library), client(client) {} 8 | 9 | // Function to recursively filter out objects with all null values 10 | nlohmann::json CommandRunner::filterNullObjects(const nlohmann::json& input) { 11 | nlohmann::json result; 12 | 13 | for (auto it = input.begin(); it != input.end(); ++it) { 14 | if (!it.value().is_null()) { 15 | if (it.value().is_object()) { 16 | // Recursively filter nested objects 17 | json nestedFiltered = filterNullObjects(it.value()); 18 | if (!nestedFiltered.empty()) { 19 | result[it.key()] = nestedFiltered; 20 | } 21 | } else { 22 | result[it.key()] = it.value(); 23 | } 24 | } 25 | } 26 | 27 | return result; 28 | } 29 | 30 | // Implement the commandToString function 31 | std::string CommandRunner::commandToString(const Command& command) { 32 | try { 33 | // Create an nlohmann::json object from the Command object 34 | nlohmann::json jsonCommand; 35 | nlohmann::json filteredJsonCommand; 36 | 37 | Bitwarden::Sdk::to_json(jsonCommand, command); 38 | 39 | filteredJsonCommand = filterNullObjects(jsonCommand); 40 | 41 | // Convert the JSON to a string 42 | std::string jsonCommandString = filteredJsonCommand.dump(); 43 | 44 | return jsonCommandString; 45 | } catch (const std::exception& ex) { 46 | std::cerr << "Error: " << ex.what() << std::endl; 47 | throw ex; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /languages/cpp/vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bitwarden-sdk-secrets", 3 | "version": "0.1.0", 4 | "homepage": "https://github.com/bitwarden/sdk-sm/tree/languages/cpp", 5 | "description": "Bitwarden Secrets Manager SDK for C++", 6 | "dependencies": [ 7 | "boost-uuid", 8 | "boost-optional", 9 | "nlohmann-json" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /languages/csharp/Bitwarden.Sdk.Samples/Bitwarden.Sdk.Samples.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /languages/csharp/Bitwarden.Sdk.Tests/Bitwarden.Sdk.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | false 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | all 18 | 19 | 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | all 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /languages/csharp/Bitwarden.Sdk.Tests/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using Xunit; 2 | -------------------------------------------------------------------------------- /languages/csharp/Bitwarden.Sdk.Tests/InteropTests.cs: -------------------------------------------------------------------------------- 1 | using Bitwarden.Sdk; 2 | using System.Diagnostics; 3 | 4 | namespace Bitwarden.Sdk.Tests; 5 | 6 | public class InteropTests 7 | { 8 | [Fact] 9 | public async void CancelingTest_ThrowsTaskCanceledException() 10 | { 11 | var client = new BitwardenClient(); 12 | 13 | var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(250)); 14 | 15 | await Assert.ThrowsAsync(async () => await client.CancellationTestAsync(cts.Token)); 16 | } 17 | 18 | [Fact] 19 | public async void NoCancel_TaskCompletesSuccessfully() 20 | { 21 | var client = new BitwardenClient(); 22 | 23 | var result = await client.CancellationTestAsync(CancellationToken.None); 24 | Assert.Equal(42, result); 25 | } 26 | 27 | [Fact] 28 | public async void Error_ThrowsException() 29 | { 30 | var client = new BitwardenClient(); 31 | 32 | var bitwardenException = await Assert.ThrowsAsync(async () => await client.ErrorTestAsync()); 33 | Assert.Equal("Internal error: This is an error.", bitwardenException.Message); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /languages/csharp/Bitwarden.Sdk.Tests/SampleTests.cs: -------------------------------------------------------------------------------- 1 | namespace Bitwarden.Sdk.Tests; 2 | 3 | public class SampleTests 4 | { 5 | [SecretsManagerFact] 6 | public async Task RunSample_Works() 7 | { 8 | // Get environment variables 9 | var identityUrl = Environment.GetEnvironmentVariable("IDENTITY_URL")!; 10 | var apiUrl = Environment.GetEnvironmentVariable("API_URL")!; 11 | var organizationId = Guid.Parse(Environment.GetEnvironmentVariable("ORGANIZATION_ID")!); 12 | var accessToken = Environment.GetEnvironmentVariable("ACCESS_TOKEN")!; 13 | var stateFile = Environment.GetEnvironmentVariable("STATE_FILE")!; 14 | 15 | // Create the SDK Client 16 | using var bitwardenClient = new BitwardenClient(new BitwardenSettings 17 | { 18 | ApiUrl = apiUrl, 19 | IdentityUrl = identityUrl 20 | }); 21 | 22 | // Authenticate 23 | await bitwardenClient.Auth.LoginAccessTokenAsync(accessToken, stateFile); 24 | 25 | // Projects Create, Update, & Get 26 | var projectResponse = await bitwardenClient.Projects.CreateAsync(organizationId, "NewTestProject"); 27 | projectResponse = await bitwardenClient.Projects.UpdateAsync(organizationId, projectResponse.Id, "NewTestProject Renamed"); 28 | projectResponse = await bitwardenClient.Projects.GetAsync(projectResponse.Id); 29 | 30 | Assert.Equal("NewTestProject Renamed", projectResponse.Name); 31 | 32 | var projectList = await bitwardenClient.Projects.ListAsync(organizationId); 33 | 34 | Assert.True(projectList.Data.Count() >= 1); 35 | 36 | // Secrets list 37 | var secretsList = await bitwardenClient.Secrets.ListAsync(organizationId); 38 | 39 | // Secrets Create, Update, Get 40 | var secretResponse = await bitwardenClient.Secrets.CreateAsync(organizationId, "New Secret", "the secret value", "the secret note", new[] { projectResponse.Id }); 41 | secretResponse = await bitwardenClient.Secrets.UpdateAsync(organizationId, secretResponse.Id, "New Secret Name", "the secret value", "the secret note", new[] { projectResponse.Id }); 42 | secretResponse = await bitwardenClient.Secrets.GetAsync(secretResponse.Id); 43 | 44 | Assert.Equal("New Secret Name", secretResponse.Key); 45 | 46 | // Secrets GetByIds 47 | var secretsResponse = await bitwardenClient.Secrets.GetByIdsAsync(new[] { secretResponse.Id }); 48 | 49 | // Secrets Sync 50 | var syncResponse = await bitwardenClient.Secrets.SyncAsync(organizationId, null); 51 | 52 | // Secrets & Projects Delete 53 | await bitwardenClient.Secrets.DeleteAsync(new[] { secretResponse.Id }); 54 | await bitwardenClient.Projects.DeleteAsync(new[] { projectResponse.Id }); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /languages/csharp/Bitwarden.Sdk.Tests/SecretsManagerFact.cs: -------------------------------------------------------------------------------- 1 | namespace Bitwarden.Sdk.Tests; 2 | 3 | public class SecretsManagerFactAttribute : FactAttribute 4 | { 5 | public SecretsManagerFactAttribute() 6 | { 7 | if (!TryGetEnvironment("IDENTITY_URL", out var identityUrl)) 8 | { 9 | Skip = "Environment variable IDENTITY_URL was not provided."; 10 | } 11 | 12 | if (!Uri.TryCreate(identityUrl, UriKind.Absolute, out _)) 13 | { 14 | Skip = $"The identity url {identityUrl} provided in IDENTITY_URL is not a valid URL."; 15 | } 16 | 17 | if (!TryGetEnvironment("API_URL", out var apiUrl)) 18 | { 19 | Skip = "Environment variable API_URL was not provided."; 20 | } 21 | 22 | if (!Uri.TryCreate(apiUrl, UriKind.Absolute, out _)) 23 | { 24 | Skip = $"The identity url {apiUrl} provided in API_URL is not a valid URL."; 25 | } 26 | 27 | if (!TryGetEnvironment("ORGANIZATION_ID", out var organizationId)) 28 | { 29 | Skip = "Environment variable ORGANIZATION_ID was not provided."; 30 | } 31 | 32 | if (!Guid.TryParse(organizationId, out _)) 33 | { 34 | Skip = $"The organization id {organizationId} provided in ORGANIZATION_ID is not a valid GUID."; 35 | } 36 | 37 | if (!TryGetEnvironment("ACCESS_TOKEN", out _)) 38 | { 39 | Skip = "Environment variable ACCESS_TOKEN was not provided."; 40 | } 41 | } 42 | 43 | private static bool TryGetEnvironment(string variable, out string value) 44 | { 45 | value = Environment.GetEnvironmentVariable(variable); 46 | 47 | if (string.IsNullOrWhiteSpace(value)) 48 | { 49 | return false; 50 | } 51 | 52 | return true; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /languages/csharp/Bitwarden.Sdk/AuthClient.cs: -------------------------------------------------------------------------------- 1 | namespace Bitwarden.Sdk; 2 | 3 | public class AuthClient 4 | { 5 | private readonly CommandRunner _commandRunner; 6 | 7 | internal AuthClient(CommandRunner commandRunner) 8 | { 9 | _commandRunner = commandRunner; 10 | } 11 | 12 | public async Task LoginAccessTokenAsync(string accessToken, string stateFile = "", CancellationToken cancellationToken = default) 13 | { 14 | var command = new Command { LoginAccessToken = new AccessTokenLoginRequest { AccessToken = accessToken, StateFile = stateFile } }; 15 | var response = await _commandRunner.RunCommandAsync(command, cancellationToken); 16 | if (response is not { Success: true }) 17 | { 18 | throw new BitwardenAuthException(response != null ? response.ErrorMessage : "Login failed"); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /languages/csharp/Bitwarden.Sdk/BitwardenAuthException.cs: -------------------------------------------------------------------------------- 1 | namespace Bitwarden.Sdk; 2 | 3 | public class BitwardenAuthException : Exception 4 | { 5 | public BitwardenAuthException(string message) : base(message) 6 | { 7 | } 8 | 9 | public BitwardenAuthException(string message, Exception innerException) 10 | : base(message, innerException) 11 | { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /languages/csharp/Bitwarden.Sdk/BitwardenClient.Debug.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.Text.Json; 4 | 5 | namespace Bitwarden.Sdk; 6 | 7 | [Obsolete("DebugCommand is intended for tests only, using any of these commands will throw errors in production code.")] 8 | [EditorBrowsable(EditorBrowsableState.Never)] 9 | partial class DebugCommand 10 | { 11 | 12 | } 13 | 14 | #if DEBUG 15 | public sealed partial class BitwardenClient 16 | { 17 | public async Task CancellationTestAsync(CancellationToken token) 18 | { 19 | var result = await _commandRunner.RunCommandAsync( 20 | new Command 21 | { 22 | Debug = new DebugCommand 23 | { 24 | CancellationTest = new CancellationTest 25 | { 26 | DurationMillis = 200, 27 | }, 28 | }, 29 | }, token); 30 | 31 | return ParseResult(result).GetInt32(); 32 | } 33 | 34 | public async Task ErrorTestAsync() 35 | { 36 | var result = await _commandRunner.RunCommandAsync( 37 | new Command 38 | { 39 | Debug = new DebugCommand 40 | { 41 | ErrorTest = new ErrorTest(), 42 | }, 43 | }, CancellationToken.None); 44 | 45 | return ParseResult(result).GetInt32(); 46 | } 47 | 48 | private JsonElement ParseResult(JsonElement result) 49 | { 50 | if (result.GetProperty("success").GetBoolean()) 51 | { 52 | return result.GetProperty("data"); 53 | } 54 | 55 | throw new BitwardenException(result.GetProperty("errorMessage").GetString()); 56 | } 57 | } 58 | #endif 59 | -------------------------------------------------------------------------------- /languages/csharp/Bitwarden.Sdk/BitwardenClient.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | 3 | namespace Bitwarden.Sdk; 4 | 5 | public sealed partial class BitwardenClient : IDisposable 6 | { 7 | private readonly CommandRunner _commandRunner; 8 | private readonly BitwardenSafeHandle _handle; 9 | 10 | public BitwardenClient(BitwardenSettings? settings = null) 11 | { 12 | var clientSettings = new ClientSettings 13 | { 14 | ApiUrl = settings?.ApiUrl!, 15 | IdentityUrl = settings?.IdentityUrl!, 16 | UserAgent = "Bitwarden DOTNET-SDK" 17 | }; 18 | 19 | _handle = BitwardenLibrary.Init(clientSettings.ToJson()); 20 | _commandRunner = new CommandRunner(_handle); 21 | Projects = new ProjectsClient(_commandRunner); 22 | Secrets = new SecretsClient(_commandRunner); 23 | Auth = new AuthClient(_commandRunner); 24 | } 25 | 26 | public ProjectsClient Projects { get; } 27 | 28 | public SecretsClient Secrets { get; } 29 | 30 | public AuthClient Auth { get; set; } 31 | 32 | public void Dispose() => _handle.Dispose(); 33 | } 34 | -------------------------------------------------------------------------------- /languages/csharp/Bitwarden.Sdk/BitwardenException.cs: -------------------------------------------------------------------------------- 1 | namespace Bitwarden.Sdk; 2 | 3 | public class BitwardenException : Exception 4 | { 5 | public BitwardenException(string message) : base(message) 6 | { 7 | } 8 | 9 | public BitwardenException(string message, Exception innerException) 10 | : base(message, innerException) 11 | { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /languages/csharp/Bitwarden.Sdk/BitwardenLibrary.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Bitwarden.Sdk; 4 | 5 | internal static partial class BitwardenLibrary 6 | { 7 | [LibraryImport("bitwarden_c", StringMarshalling = StringMarshalling.Utf8)] 8 | private static partial BitwardenSafeHandle init(string settings); 9 | 10 | [LibraryImport("bitwarden_c", StringMarshalling = StringMarshalling.Utf8)] 11 | private static partial void free_mem(IntPtr handle); 12 | 13 | [LibraryImport("bitwarden_c", StringMarshalling = StringMarshalling.Utf8)] 14 | private static partial string run_command(string json, BitwardenSafeHandle handle); 15 | 16 | internal delegate void OnCompleteCallback(IntPtr json); 17 | 18 | [LibraryImport("bitwarden_c", StringMarshalling = StringMarshalling.Utf8)] 19 | private static partial IntPtr run_command_async(string json, 20 | BitwardenSafeHandle handle, 21 | OnCompleteCallback onCompletedCallback, 22 | [MarshalAs(UnmanagedType.U1)] bool isCancellable); 23 | 24 | [LibraryImport("bitwarden_c", StringMarshalling = StringMarshalling.Utf8)] 25 | private static partial void abort_and_free_handle(IntPtr joinHandle); 26 | 27 | [LibraryImport("bitwarden_c", StringMarshalling = StringMarshalling.Utf8)] 28 | private static partial void free_handle(IntPtr joinHandle); 29 | 30 | internal static BitwardenSafeHandle Init(string settings) => init(settings); 31 | 32 | internal static void FreeMemory(IntPtr handle) => free_mem(handle); 33 | 34 | internal static string RunCommand(string json, BitwardenSafeHandle handle) => run_command(json, handle); 35 | 36 | internal static Task RunCommandAsync(string json, BitwardenSafeHandle handle, CancellationToken cancellationToken) 37 | { 38 | cancellationToken.ThrowIfCancellationRequested(); 39 | var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); 40 | 41 | IntPtr abortPointer = IntPtr.Zero; 42 | 43 | try 44 | { 45 | 46 | abortPointer = run_command_async(json, handle, (resultPointer) => 47 | { 48 | var stringResult = Marshal.PtrToStringUTF8(resultPointer); 49 | tcs.SetResult(stringResult); 50 | 51 | if (abortPointer != IntPtr.Zero) 52 | { 53 | free_handle(abortPointer); 54 | } 55 | }, cancellationToken.CanBeCanceled); 56 | } 57 | catch (Exception ex) 58 | { 59 | tcs.SetException(ex); 60 | } 61 | 62 | cancellationToken.Register((state) => 63 | { 64 | // This register delegate will never be called unless the token is cancelable 65 | // therefore we know that the abortPointer is a valid pointer. 66 | abort_and_free_handle((IntPtr)state); 67 | tcs.SetCanceled(cancellationToken); 68 | }, abortPointer); 69 | 70 | return tcs.Task; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /languages/csharp/Bitwarden.Sdk/BitwardenSafeHandle.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32.SafeHandles; 2 | 3 | namespace Bitwarden.Sdk; 4 | 5 | internal class BitwardenSafeHandle : SafeHandleZeroOrMinusOneIsInvalid 6 | { 7 | public BitwardenSafeHandle() : base(true) 8 | { 9 | SetHandle(handle); 10 | } 11 | 12 | protected override bool ReleaseHandle() 13 | { 14 | BitwardenLibrary.FreeMemory(handle); 15 | return true; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /languages/csharp/Bitwarden.Sdk/BitwardenSettings.cs: -------------------------------------------------------------------------------- 1 | namespace Bitwarden.Sdk; 2 | 3 | public class BitwardenSettings 4 | { 5 | /// 6 | /// The api url of the targeted Bitwarden instance. Defaults to `https://api.bitwarden.com` 7 | /// 8 | public string? ApiUrl { get; set; } 9 | 10 | /// 11 | /// The identity url of the targeted Bitwarden instance. Defaults to `https://identity.bitwarden.com` 12 | /// 13 | public string? IdentityUrl { get; set; } 14 | } 15 | -------------------------------------------------------------------------------- /languages/csharp/Bitwarden.Sdk/CommandRunner.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | 3 | namespace Bitwarden.Sdk; 4 | 5 | internal class CommandRunner 6 | { 7 | private readonly BitwardenSafeHandle _handle; 8 | 9 | internal CommandRunner(BitwardenSafeHandle handle) 10 | { 11 | _handle = handle; 12 | } 13 | 14 | internal T? RunCommand(Command command) 15 | { 16 | var req = JsonSerializer.Serialize(command, Converter.Settings); 17 | var result = BitwardenLibrary.RunCommand(req, _handle); 18 | return JsonSerializer.Deserialize(result, Converter.Settings); 19 | } 20 | 21 | internal async Task RunCommandAsync(Command command, CancellationToken cancellationToken) 22 | { 23 | var req = JsonSerializer.Serialize(command, Converter.Settings); 24 | var result = await BitwardenLibrary.RunCommandAsync(req, _handle, cancellationToken); 25 | return JsonSerializer.Deserialize(result, Converter.Settings); 26 | } 27 | 28 | internal async Task RunCommandAsync(string command, CancellationToken cancellationToken) 29 | { 30 | var result = await BitwardenLibrary.RunCommandAsync(command, _handle, cancellationToken); 31 | return JsonSerializer.Deserialize(result, Converter.Settings); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /languages/csharp/Bitwarden.Sdk/bitwarden.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitwarden/sdk-sm/e9a98cddda1cdf417b9c1d4872b3ed6bcb2beec9/languages/csharp/Bitwarden.Sdk/bitwarden.png -------------------------------------------------------------------------------- /languages/csharp/Bitwarden.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bitwarden.Sdk", "Bitwarden.Sdk\Bitwarden.Sdk.csproj", "{DADE59E5-E573-430A-8EB2-BC21D8E8C1D3}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bitwarden.Sdk.Samples", "Bitwarden.Sdk.Samples\Bitwarden.Sdk.Samples.csproj", "{CA9F8EDC-643F-4624-AC00-F741E1F30CA4}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bitwarden.Sdk.Tests", "Bitwarden.Sdk.Tests\Bitwarden.Sdk.Tests.csproj", "{6E62CBBF-E9E6-4661-A3DC-D89C18E15A89}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {DADE59E5-E573-430A-8EB2-BC21D8E8C1D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {DADE59E5-E573-430A-8EB2-BC21D8E8C1D3}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {DADE59E5-E573-430A-8EB2-BC21D8E8C1D3}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {DADE59E5-E573-430A-8EB2-BC21D8E8C1D3}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {CA9F8EDC-643F-4624-AC00-F741E1F30CA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {CA9F8EDC-643F-4624-AC00-F741E1F30CA4}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {CA9F8EDC-643F-4624-AC00-F741E1F30CA4}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {CA9F8EDC-643F-4624-AC00-F741E1F30CA4}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {6E62CBBF-E9E6-4661-A3DC-D89C18E15A89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {6E62CBBF-E9E6-4661-A3DC-D89C18E15A89}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {6E62CBBF-E9E6-4661-A3DC-D89C18E15A89}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {6E62CBBF-E9E6-4661-A3DC-D89C18E15A89}.Release|Any CPU.Build.0 = Release|Any CPU 28 | EndGlobalSection 29 | EndGlobal 30 | -------------------------------------------------------------------------------- /languages/csharp/README.md: -------------------------------------------------------------------------------- 1 | # Bitwarden Secrets Manager SDK 2 | 3 | .NET bindings for interacting with the [Bitwarden Secrets Manager]. This is a beta release and might be missing some functionality. 4 | 5 | ## Create access token 6 | 7 | Review the help documentation on [Access Tokens] 8 | 9 | ## Usage code snippets 10 | 11 | ### Create new Bitwarden client 12 | 13 | ```csharp 14 | const string accessToken = ""; 15 | const string stateFile = ""; 16 | 17 | using var bitwardenClient = new BitwardenClient(new BitwardenSettings 18 | { 19 | ApiUrl = apiUrl, 20 | IdentityUrl = identityUrl 21 | }); 22 | 23 | bitwardenClient.LoginAccessToken(accessToken, stateFile); 24 | ``` 25 | 26 | ### Create new project 27 | 28 | ```csharp 29 | var organizationId = Guid.Parse(""); 30 | var projectResponse = bitwardenClient.Projects().Create(organizationId, "TestProject"); 31 | ``` 32 | 33 | ### List all projects 34 | 35 | ```csharp 36 | var response = bitwardenClient.Projects.List(organizationId); 37 | ``` 38 | 39 | ### Update project 40 | 41 | ```csharp 42 | var projectId = projectResponse.Id; 43 | projectResponse = bitwardenClient.Projects.Get(projectId); 44 | projectResponse = bitwardenClient.Projects.Update(organizationId, projectId, "TestProjectUpdated"); 45 | ``` 46 | 47 | ### Add new secret 48 | 49 | ```csharp 50 | var key = "key"; 51 | var value = "value"; 52 | var note = "note"; 53 | var secretResponse = bitwardenClient.Secrets.Create(organizationId, key, value, note, new[] { projectId }); 54 | ``` 55 | 56 | ### Update secret 57 | ```csharp 58 | var secretId = secretResponse.Id; 59 | secretResponse = bitwardenClient.Secrets.Get(secretId); 60 | secretResponse = bitwardenClient.Secrets.Update(organizationId, secretId, "key2", "value2", "note2", new[] { projectId }); 61 | ``` 62 | 63 | ### Secret GetByIds 64 | 65 | ```csharp 66 | var secretsResponse = bitwardenClient.Secrets.GetByIds(new[] { secretResponse.Id }); 67 | ``` 68 | 69 | ### List secrets 70 | 71 | ```csharp 72 | var secretIdentifiersResponse = bitwardenClient.Secrets.List(organizationId); 73 | ``` 74 | 75 | ### Sync secrets 76 | 77 | ```csharp 78 | var syncResponse = bitwardenClient.Secrets.Sync(organizationId, null); 79 | ``` 80 | 81 | # Delete secret or project 82 | 83 | ```csharp 84 | bitwardenClient.Secrets.Delete(new [] { secretId }); 85 | bitwardenClient.Projects.Delete(new [] { projectId }); 86 | ``` 87 | 88 | [Access Tokens]: https://bitwarden.com/help/access-tokens/ 89 | [Bitwarden Secrets Manager]: https://bitwarden.com/products/secrets-manager/ 90 | -------------------------------------------------------------------------------- /languages/csharp/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "8.0.100", 4 | "rollForward": "latestFeature" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /languages/go/.version: -------------------------------------------------------------------------------- 1 | 1.0.2 -------------------------------------------------------------------------------- /languages/go/bitwarden_client.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/bitwarden/sdk-go/internal/cinterface" 7 | ) 8 | 9 | type BitwardenClientInterface interface { 10 | AccessTokenLogin(accessToken string, stateFile *string) error 11 | Projects() ProjectsInterface 12 | Secrets() SecretsInterface 13 | Generators() GeneratorsInterface 14 | Close() 15 | } 16 | 17 | type BitwardenClient struct { 18 | client cinterface.ClientPointer 19 | lib cinterface.BitwardenLibrary 20 | commandRunner CommandRunnerInterface 21 | projects ProjectsInterface 22 | secrets SecretsInterface 23 | generators GeneratorsInterface 24 | } 25 | 26 | func NewBitwardenClient(apiURL *string, identityURL *string) (BitwardenClientInterface, error) { 27 | deviceType := DeviceType("SDK") 28 | userAgent := "Bitwarden GOLANG-SDK" 29 | clientSettings := ClientSettings{ 30 | APIURL: apiURL, 31 | IdentityURL: identityURL, 32 | UserAgent: &userAgent, 33 | DeviceType: &deviceType, 34 | } 35 | 36 | settingsJSON, err := json.Marshal(clientSettings) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | lib := cinterface.NewBitwardenLibrary() 42 | client, err := lib.Init(string(settingsJSON)) 43 | if err != nil { 44 | return nil, err 45 | } 46 | runner := NewCommandRunner(client, lib) 47 | 48 | return &BitwardenClient{ 49 | lib: lib, 50 | client: client, 51 | commandRunner: runner, 52 | projects: NewProjects(runner), 53 | secrets: NewSecrets(runner), 54 | generators: NewGenerators(runner), 55 | }, nil 56 | } 57 | 58 | func (c *BitwardenClient) AccessTokenLogin(accessToken string, stateFile *string) error { 59 | req := AccessTokenLoginRequest{AccessToken: accessToken, StateFile: stateFile} 60 | command := Command{LoginAccessToken: &req} 61 | 62 | responseStr, err := c.commandRunner.RunCommand(command) 63 | if err != nil { 64 | return err 65 | } 66 | 67 | var response APIKeyLoginResponse 68 | return checkSuccessAndError(responseStr, &response) 69 | } 70 | 71 | func (c *BitwardenClient) Projects() ProjectsInterface { 72 | return c.projects 73 | } 74 | 75 | func (c *BitwardenClient) Secrets() SecretsInterface { 76 | return c.secrets 77 | } 78 | 79 | func (c *BitwardenClient) Generators() GeneratorsInterface { 80 | return c.generators 81 | } 82 | 83 | func (c *BitwardenClient) Close() { 84 | c.lib.FreeMem(c.client) 85 | } 86 | -------------------------------------------------------------------------------- /languages/go/command_runner.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/bitwarden/sdk-go/internal/cinterface" 7 | ) 8 | 9 | type CommandRunnerInterface interface { 10 | RunCommand(command Command) (string, error) 11 | } 12 | 13 | type CommandRunner struct { 14 | client cinterface.ClientPointer 15 | lib cinterface.BitwardenLibrary 16 | } 17 | 18 | func NewCommandRunner(client cinterface.ClientPointer, lib cinterface.BitwardenLibrary) *CommandRunner { 19 | return &CommandRunner{ 20 | client: client, 21 | lib: lib, 22 | } 23 | } 24 | 25 | func (c *CommandRunner) RunCommand(command Command) (string, error) { 26 | commandJSON, err := json.Marshal(command) 27 | if err != nil { 28 | return "", err 29 | } 30 | 31 | responseStr, err := c.lib.RunCommand(string(commandJSON), c.client) 32 | if err != nil { 33 | return "", err 34 | } 35 | 36 | return responseStr, nil 37 | } 38 | -------------------------------------------------------------------------------- /languages/go/example/go.mod: -------------------------------------------------------------------------------- 1 | module example 2 | 3 | replace github.com/bitwarden/sdk-go => ../ 4 | 5 | go 1.21 6 | 7 | require ( 8 | github.com/bitwarden/sdk-go v0.0.0-00010101000000-000000000000 9 | github.com/gofrs/uuid v4.4.0+incompatible 10 | ) 11 | -------------------------------------------------------------------------------- /languages/go/example/go.sum: -------------------------------------------------------------------------------- 1 | github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= 2 | github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 3 | -------------------------------------------------------------------------------- /languages/go/generators.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | type GeneratorsInterface interface { 4 | GeneratePassword(request PasswordGeneratorRequest) (*string, error) 5 | } 6 | 7 | type Generators struct { 8 | CommandRunner CommandRunnerInterface 9 | } 10 | 11 | func NewGenerators(commandRunner CommandRunnerInterface) *Generators { 12 | return &Generators{CommandRunner: commandRunner} 13 | } 14 | 15 | func (s *Generators) executeCommand(command Command, target interface{}) error { 16 | responseStr, err := s.CommandRunner.RunCommand(command) 17 | if err != nil { 18 | return err 19 | } 20 | return checkSuccessAndError(responseStr, target) 21 | } 22 | 23 | func (s *Generators) GeneratePassword(request PasswordGeneratorRequest) (*string, error) { 24 | command := Command{ 25 | Generators: &GeneratorsCommand{ 26 | GeneratePassword: request, 27 | }, 28 | } 29 | 30 | var response string 31 | if err := s.executeCommand(command, &response); err != nil { 32 | return nil, err 33 | } 34 | return &response, nil 35 | } 36 | -------------------------------------------------------------------------------- /languages/go/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitwarden/sdk-go 2 | 3 | go 1.21 4 | -------------------------------------------------------------------------------- /languages/go/go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitwarden/sdk-sm/e9a98cddda1cdf417b9c1d4872b3ed6bcb2beec9/languages/go/go.sum -------------------------------------------------------------------------------- /languages/go/internal/cinterface/bitwarden_library.go: -------------------------------------------------------------------------------- 1 | package cinterface 2 | 3 | import ( 4 | "fmt" 5 | "unsafe" 6 | ) 7 | 8 | /* 9 | #cgo LDFLAGS: -lbitwarden_c 10 | #cgo linux,amd64 LDFLAGS: -L ./lib/linux-x64 11 | #cgo linux,arm64 LDFLAGS: -L ./lib/linux-arm64 12 | #cgo darwin,amd64 LDFLAGS: -L ./lib/darwin-x64 -framework Security -framework SystemConfiguration 13 | #cgo darwin,arm64 LDFLAGS: -L ./lib/darwin-arm64 -framework Security -framework SystemConfiguration 14 | #cgo windows,amd64 LDFLAGS: -L ./lib/windows-x64 -lbitwarden_c -ladvapi32 -lbcrypt -lcrypt32 -lcryptnet -lkernel32 -lncrypt -lntdll -luserenv -lws2_32 -lmsvcrt -loleaut32 -lruntimeobject 15 | #include 16 | typedef void* ClientPtr; 17 | extern char* run_command(const char *command, ClientPtr client); 18 | extern ClientPtr init(const char *clientSettings); 19 | extern void free_mem(ClientPtr client); 20 | */ 21 | import "C" 22 | 23 | type ClientPointer struct { 24 | Pointer C.ClientPtr 25 | } 26 | 27 | type BitwardenLibrary interface { 28 | Init(clientSettings string) (ClientPointer, error) 29 | FreeMem(client ClientPointer) 30 | RunCommand(command string, client ClientPointer) (string, error) 31 | } 32 | 33 | type BitwardenLibraryImpl struct{} 34 | 35 | func NewBitwardenLibrary() BitwardenLibrary { 36 | return &BitwardenLibraryImpl{} 37 | } 38 | 39 | func (b *BitwardenLibraryImpl) Init(clientSettings string) (ClientPointer, error) { 40 | ptr := C.init(C.CString(clientSettings)) 41 | if ptr == nil { 42 | return ClientPointer{}, fmt.Errorf("initialization failed") 43 | } 44 | return ClientPointer{Pointer: ptr}, nil 45 | } 46 | 47 | func (b *BitwardenLibraryImpl) FreeMem(client ClientPointer) { 48 | C.free_mem(client.Pointer) 49 | } 50 | 51 | func (b *BitwardenLibraryImpl) RunCommand(command string, client ClientPointer) (string, error) { 52 | cstr := C.run_command(C.CString(command), client.Pointer) 53 | if cstr == nil { 54 | return "", fmt.Errorf("run command failed") 55 | } 56 | defer C.free(unsafe.Pointer(cstr)) 57 | return C.GoString(cstr), nil 58 | } 59 | -------------------------------------------------------------------------------- /languages/go/project.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | type ProjectsInterface interface { 4 | Create(organizationID string, name string) (*ProjectResponse, error) 5 | List(organizationID string) (*ProjectsResponse, error) 6 | Get(projectID string) (*ProjectResponse, error) 7 | Update(projectID string, organizationID string, name string) (*ProjectResponse, error) 8 | Delete(projectIDs []string) (*ProjectsDeleteResponse, error) 9 | } 10 | 11 | type Projects struct { 12 | CommandRunner CommandRunnerInterface 13 | } 14 | 15 | func NewProjects(commandRunner CommandRunnerInterface) *Projects { 16 | return &Projects{CommandRunner: commandRunner} 17 | } 18 | 19 | func (p *Projects) Get(id string) (*ProjectResponse, error) { 20 | command := Command{ 21 | Projects: &ProjectsCommand{ 22 | Get: &ProjectGetRequest{ 23 | ID: id, 24 | }, 25 | }, 26 | } 27 | var response ProjectResponse 28 | if err := p.executeCommand(command, &response); err != nil { 29 | return nil, err 30 | } 31 | return &response, nil 32 | } 33 | 34 | func (p *Projects) Create(organizationID string, name string) (*ProjectResponse, error) { 35 | command := Command{ 36 | Projects: &ProjectsCommand{ 37 | Create: &ProjectCreateRequest{ 38 | OrganizationID: organizationID, 39 | Name: name, 40 | }, 41 | }, 42 | } 43 | 44 | var response ProjectResponse 45 | if err := p.executeCommand(command, &response); err != nil { 46 | return nil, err 47 | } 48 | return &response, nil 49 | } 50 | 51 | func (p *Projects) List(organizationID string) (*ProjectsResponse, error) { 52 | command := Command{ 53 | Projects: &ProjectsCommand{ 54 | List: &ProjectsListRequest{ 55 | OrganizationID: organizationID, 56 | }, 57 | }, 58 | } 59 | 60 | var response ProjectsResponse 61 | if err := p.executeCommand(command, &response); err != nil { 62 | return nil, err 63 | } 64 | return &response, nil 65 | } 66 | 67 | func (p *Projects) Update(projectID, organizationID, name string) (*ProjectResponse, error) { 68 | command := Command{ 69 | Projects: &ProjectsCommand{ 70 | Update: &ProjectPutRequest{ 71 | ID: projectID, 72 | OrganizationID: organizationID, 73 | Name: name, 74 | }, 75 | }, 76 | } 77 | 78 | var response ProjectResponse 79 | if err := p.executeCommand(command, &response); err != nil { 80 | return nil, err 81 | } 82 | return &response, nil 83 | } 84 | 85 | func (p *Projects) Delete(projectIDs []string) (*ProjectsDeleteResponse, error) { 86 | command := Command{ 87 | Projects: &ProjectsCommand{ 88 | Delete: &ProjectsDeleteRequest{ 89 | IDS: projectIDs, 90 | }, 91 | }, 92 | } 93 | 94 | var response ProjectsDeleteResponse 95 | if err := p.executeCommand(command, &response); err != nil { 96 | return nil, err 97 | } 98 | return &response, nil 99 | } 100 | 101 | func (p *Projects) executeCommand(command Command, target interface{}) error { 102 | responseStr, err := p.CommandRunner.RunCommand(command) 103 | if err != nil { 104 | return err 105 | } 106 | return checkSuccessAndError(responseStr, target) 107 | } 108 | -------------------------------------------------------------------------------- /languages/go/util.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | func checkSuccessAndError(responseStr string, v interface{}) error { 9 | var wrapper struct { 10 | Success bool `json:"success"` 11 | ErrorMessage *string `json:"errorMessage"` 12 | Data *json.RawMessage `json:"data"` 13 | } 14 | 15 | err := json.Unmarshal([]byte(responseStr), &wrapper) 16 | if err != nil { 17 | return fmt.Errorf("failed to unmarshal wrapper response: %v", err) 18 | } 19 | 20 | if !wrapper.Success { 21 | if wrapper.ErrorMessage != nil { 22 | return fmt.Errorf("API error: %s", *wrapper.ErrorMessage) 23 | } 24 | return fmt.Errorf("API error: unknown") 25 | } 26 | 27 | err = json.Unmarshal(*wrapper.Data, &v) 28 | if err != nil { 29 | return fmt.Errorf("failed to unmarshal response: %v", err) 30 | } 31 | 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /languages/java/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .gradle 3 | src/main/resources 4 | -------------------------------------------------------------------------------- /languages/java/INSTALL.md: -------------------------------------------------------------------------------- 1 | # Java build 2 | 3 | ## Introduction 4 | 5 | Gradle is used to build Java Bitwarden client library. 6 | 7 | The output of the build is placed in `build/libs` directory and should contain `BitwardenSDK.jar` file. 8 | 9 | ## Prerequisites 10 | 11 | - JDK 17 installed. 12 | - Bitwarden SDK native library build. See [SDK README.md](../../README.md) for instructions. 13 | 14 | ## Build Commands 15 | 16 | ```shell 17 | ./gradlew build 18 | ``` 19 | 20 | ## Example 21 | 22 | ### macOS 23 | 24 | #### Install Prerequisites 25 | 26 | Use brew to install JDK 17. 27 | 28 | ```shell 29 | brew install --cask temurin@17 30 | brew install jenv 31 | export PATH="$HOME/.jenv/bin:$PATH" 32 | eval "$(jenv init -)" 33 | jenv add /Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home 34 | jenv shell 17 35 | ``` 36 | 37 | #### Build Commands 38 | 39 | ```shell 40 | ./gradlew build 41 | ``` 42 | 43 | ## Example SDK Usage Project 44 | 45 | ```shell 46 | export ACCESS_TOKEN="" 47 | export ORGANIZATION_ID="" 48 | export API_URL="https://api.bitwarden.com" 49 | export IDENTITY_URL="https://identity.bitwarden.com" 50 | 51 | ./gradlew :example:run 52 | ``` 53 | -------------------------------------------------------------------------------- /languages/java/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | */ 4 | 5 | plugins { 6 | id 'java-library' 7 | id 'maven-publish' 8 | } 9 | 10 | repositories { 11 | mavenLocal() 12 | maven { 13 | url = uri('https://repo.maven.apache.org/maven2/') 14 | } 15 | 16 | dependencies { 17 | api 'com.fasterxml.jackson.core:jackson-core:2.9.10' 18 | api 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.10' 19 | api 'net.java.dev.jna:jna-platform:5.12.1' 20 | } 21 | 22 | description = 'Bitwarden Secrets Manager Java SDK' 23 | java.sourceCompatibility = JavaVersion.VERSION_1_8 24 | 25 | publishing { 26 | publications { 27 | maven(MavenPublication) { 28 | groupId = 'com.bitwarden' 29 | artifactId = 'sdk-secrets' 30 | 31 | // Determine the version from the git history. 32 | // 33 | // PRs: use the branch name. 34 | // Main: Grab it from `crates/bitwarden/Cargo.toml` 35 | 36 | def branchName = "git branch --show-current".execute().text.trim() 37 | version = "1.0.1" 38 | 39 | afterEvaluate { 40 | from components.java 41 | } 42 | } 43 | } 44 | repositories { 45 | maven { 46 | name = "GitHubPackages" 47 | url = "https://maven.pkg.github.com/bitwarden/sdk-sm" 48 | credentials { 49 | username = System.getenv("GITHUB_ACTOR") 50 | password = System.getenv("GITHUB_TOKEN") 51 | } 52 | } 53 | maven { 54 | name = "OSSRH" 55 | url = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" 56 | credentials { 57 | username = System.getenv("MAVEN_USERNAME") 58 | password = System.getenv("MAVEN_PASSWORD") 59 | } 60 | } 61 | } 62 | } 63 | } 64 | 65 | tasks.withType(JavaCompile) { 66 | options.encoding = 'UTF-8' 67 | } 68 | 69 | tasks.withType(Javadoc) { 70 | options.encoding = 'UTF-8' 71 | failOnError = false 72 | } 73 | 74 | java { 75 | withJavadocJar() 76 | withSourcesJar() 77 | } 78 | 79 | jar { 80 | // Copy native library to jar resources for local gradle build 81 | if (System.getenv("GITHUB_TOKEN") == null) { 82 | from('../../target/debug') { 83 | include '*libbitwarden_c*.dylib' 84 | into "darwin-x86-64" 85 | } 86 | from('../../target/debug') { 87 | include '*libbitwarden_c*.dylib' 88 | into "darwin-aarch64" 89 | } 90 | from('../../target/debug') { 91 | include '*libbitwarden_c*.so' 92 | into "linux-x86-64" 93 | } 94 | from('../../target/debug') { 95 | include '*bitwarden_c*.dll' 96 | into "win32-x86-64" 97 | } 98 | } 99 | } 100 | 101 | -------------------------------------------------------------------------------- /languages/java/example/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'application' 3 | } 4 | 5 | repositories { 6 | mavenLocal() 7 | mavenCentral() 8 | } 9 | 10 | dependencies { 11 | implementation rootProject 12 | } 13 | 14 | application { 15 | mainClass = 'Example' 16 | } 17 | 18 | sourceSets { 19 | main { 20 | java.srcDirs += '.' 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /languages/java/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitwarden/sdk-sm/e9a98cddda1cdf417b9c1d4872b3ed6bcb2beec9/languages/java/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /languages/java/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /languages/java/settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | */ 4 | 5 | rootProject.name = 'BitwardenSDK' 6 | 7 | include "example" 8 | -------------------------------------------------------------------------------- /languages/java/src/main/java/com/bitwarden/sdk/AuthClient.java: -------------------------------------------------------------------------------- 1 | package com.bitwarden.sdk; 2 | 3 | import com.bitwarden.sdk.schema.*; 4 | 5 | import java.util.function.Function; 6 | 7 | public class AuthClient { 8 | 9 | private final CommandRunner commandRunner; 10 | 11 | AuthClient(CommandRunner commandRunner) { 12 | this.commandRunner = commandRunner; 13 | } 14 | 15 | public APIKeyLoginResponse loginAccessToken(String accessToken, String stateFile) { 16 | Command command = new Command(); 17 | AccessTokenLoginRequest accessTokenLoginRequest = new AccessTokenLoginRequest(); 18 | accessTokenLoginRequest.setAccessToken(accessToken); 19 | accessTokenLoginRequest.setStateFile(stateFile); 20 | 21 | command.setLoginAccessToken(accessTokenLoginRequest); 22 | 23 | ResponseForAPIKeyLoginResponse response = commandRunner.runCommand(command, 24 | BitwardenClient.throwingFunctionWrapper(Converter::ResponseForAPIKeyLoginResponseFromJsonString)); 25 | 26 | if (response == null || !response.getSuccess()) { 27 | throw new BitwardenClientException(response != null ? response.getErrorMessage() : "Login failed"); 28 | } 29 | 30 | return response.getData(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /languages/java/src/main/java/com/bitwarden/sdk/BitwardenClient.java: -------------------------------------------------------------------------------- 1 | package com.bitwarden.sdk; 2 | 3 | import com.bitwarden.sdk.schema.*; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.sun.jna.Native; 6 | import com.sun.jna.Pointer; 7 | 8 | import java.util.function.Function; 9 | 10 | public class BitwardenClient implements AutoCloseable { 11 | 12 | private final Pointer client; 13 | 14 | private final BitwardenLibrary library; 15 | 16 | private final CommandRunner commandRunner; 17 | 18 | private boolean isClientOpen; 19 | 20 | private final ProjectsClient projects; 21 | 22 | private final SecretsClient secrets; 23 | 24 | private final AuthClient auth; 25 | 26 | public BitwardenClient(BitwardenSettings bitwardenSettings) { 27 | ClientSettings clientSettings = new ClientSettings(); 28 | clientSettings.setAPIURL(bitwardenSettings.getApiUrl()); 29 | clientSettings.setIdentityURL(bitwardenSettings.getIdentityUrl()); 30 | clientSettings.setDeviceType(DeviceType.SDK); 31 | clientSettings.setUserAgent("Bitwarden JAVA-SDK"); 32 | 33 | library = Native.load("bitwarden_c", BitwardenLibrary.class); 34 | 35 | try { 36 | client = library.init(Converter.ClientSettingsToJsonString(clientSettings)); 37 | } catch (JsonProcessingException e) { 38 | throw new BitwardenClientException("Error while processing client settings"); 39 | } 40 | 41 | commandRunner = new CommandRunner(library, client); 42 | projects = new ProjectsClient(commandRunner); 43 | secrets = new SecretsClient(commandRunner); 44 | auth = new AuthClient(commandRunner); 45 | isClientOpen = true; 46 | } 47 | 48 | static Function throwingFunctionWrapper(ThrowingFunction throwingFunction) { 49 | return i -> { 50 | try { 51 | return throwingFunction.accept(i); 52 | } catch (Exception ex) { 53 | throw new BitwardenClientException("Response failed", ex); 54 | } 55 | }; 56 | } 57 | 58 | public ProjectsClient projects() { 59 | return projects; 60 | } 61 | 62 | public SecretsClient secrets() { 63 | return secrets; 64 | } 65 | 66 | public AuthClient auth() { 67 | return auth; 68 | } 69 | 70 | @Override 71 | public void close() { 72 | if (isClientOpen) { 73 | library.free_mem(client); 74 | isClientOpen = false; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /languages/java/src/main/java/com/bitwarden/sdk/BitwardenClientException.java: -------------------------------------------------------------------------------- 1 | package com.bitwarden.sdk; 2 | 3 | public class BitwardenClientException extends RuntimeException { 4 | 5 | public BitwardenClientException(String message) { 6 | super(message); 7 | } 8 | 9 | public BitwardenClientException(String message, Exception ex) { 10 | super(message, ex); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /languages/java/src/main/java/com/bitwarden/sdk/BitwardenLibrary.java: -------------------------------------------------------------------------------- 1 | package com.bitwarden.sdk; 2 | 3 | import com.sun.jna.Library; 4 | import com.sun.jna.Pointer; 5 | 6 | public interface BitwardenLibrary extends Library { 7 | 8 | Pointer init(String clientSettings); 9 | 10 | void free_mem(Pointer client); 11 | 12 | String run_command(String command, Pointer client); 13 | } 14 | -------------------------------------------------------------------------------- /languages/java/src/main/java/com/bitwarden/sdk/BitwardenSettings.java: -------------------------------------------------------------------------------- 1 | package com.bitwarden.sdk; 2 | 3 | public class BitwardenSettings { 4 | 5 | private String apiUrl; 6 | 7 | private String identityUrl; 8 | 9 | public BitwardenSettings() { 10 | } 11 | 12 | public BitwardenSettings(String apiUrl, String identityUrl) { 13 | this.apiUrl = apiUrl; 14 | this.identityUrl = identityUrl; 15 | } 16 | 17 | public String getApiUrl() { 18 | return apiUrl; 19 | } 20 | 21 | public void setApiUrl(String apiUrl) { 22 | this.apiUrl = apiUrl; 23 | } 24 | 25 | public String getIdentityUrl() { 26 | return identityUrl; 27 | } 28 | 29 | public void setIdentityUrl(String identityUrl) { 30 | this.identityUrl = identityUrl; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /languages/java/src/main/java/com/bitwarden/sdk/CommandRunner.java: -------------------------------------------------------------------------------- 1 | package com.bitwarden.sdk; 2 | 3 | import com.bitwarden.sdk.schema.Command; 4 | import com.bitwarden.sdk.schema.Converter; 5 | import com.fasterxml.jackson.annotation.JsonInclude; 6 | import com.fasterxml.jackson.databind.ObjectMapper; 7 | import com.sun.jna.Pointer; 8 | 9 | import java.io.IOException; 10 | import java.util.function.Function; 11 | 12 | class CommandRunner { 13 | 14 | private final BitwardenLibrary library; 15 | 16 | private final Pointer client; 17 | 18 | CommandRunner(BitwardenLibrary library, Pointer client) { 19 | this.library = library; 20 | this.client = client; 21 | } 22 | 23 | T runCommand(Command command, Function deserializer) { 24 | String response = null; 25 | 26 | try { 27 | response = library.run_command(commandToString(command), client); 28 | } catch (IOException e) { 29 | throw new RuntimeException(e); 30 | } 31 | 32 | return deserializer.apply(response); 33 | } 34 | 35 | private String commandToString(Command command) throws IOException { 36 | // Removes null properties from the generated converter output to avoid command errors 37 | String inputJson = Converter.CommandToJsonString(command); 38 | 39 | ObjectMapper mapper = new ObjectMapper(); 40 | mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); 41 | 42 | Object inputObject = mapper.readValue(inputJson, Object.class); 43 | return mapper.writeValueAsString(inputObject); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /languages/java/src/main/java/com/bitwarden/sdk/ThrowingFunction.java: -------------------------------------------------------------------------------- 1 | package com.bitwarden.sdk; 2 | 3 | @FunctionalInterface 4 | public interface ThrowingFunction { 5 | 6 | R accept(T t) throws E; 7 | } 8 | -------------------------------------------------------------------------------- /languages/js/example/index.js: -------------------------------------------------------------------------------- 1 | const { BitwardenClient: BitwardenClientWasm, LogLevel } = require("@bitwarden/sdk-wasm"); 2 | const sdk = require("@bitwarden/sdk-client"); 3 | 4 | async function main() { 5 | const settings = { 6 | apiUrl: process.env.API_URL, 7 | identityUrl: process.env.IDENTITY_URL, 8 | }; 9 | 10 | const client = new sdk.BitwardenClient( 11 | new BitwardenClientWasm(JSON.stringify(settings), LogLevel.Debug), 12 | ); 13 | 14 | const organization_id = process.env.ORGANIZATION_ID; 15 | await client.accessTokenLogin(process.env.ACCESS_TOKEN); 16 | 17 | const project = await client.projects().create("test", organization_id); 18 | const projects = await client.projects().list(organization_id); 19 | console.log(projects.data); 20 | 21 | const secret = await client 22 | .secrets() 23 | .create("test-secret", "My secret!", "This is my secret", [project.id], organization_id); 24 | const secrets = await client.secrets().list(organization_id); 25 | console.log(secrets.data); 26 | 27 | console.log(project, secret); 28 | 29 | await client.projects().delete([project.id]); 30 | await client.secrets().delete([secret.id]); 31 | } 32 | main(); 33 | -------------------------------------------------------------------------------- /languages/js/example/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sdk-example", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "name": "sdk-example", 8 | "dependencies": { 9 | "@bitwarden/sdk-client": "../sdk-client", 10 | "@bitwarden/sdk-wasm": "../wasm" 11 | } 12 | }, 13 | "../sdk-client": { 14 | "name": "@bitwarden/sdk-client", 15 | "devDependencies": { 16 | "@types/node": "^20.0.0", 17 | "rimraf": "^6.0.0", 18 | "typescript": "^5.0.3" 19 | } 20 | }, 21 | "../wasm": { 22 | "name": "@bitwarden/sdk-wasm", 23 | "version": "0.1.0" 24 | }, 25 | "node_modules/@bitwarden/sdk-client": { 26 | "resolved": "../sdk-client", 27 | "link": true 28 | }, 29 | "node_modules/@bitwarden/sdk-wasm": { 30 | "resolved": "../wasm", 31 | "link": true 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /languages/js/example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sdk-example", 3 | "main": "index.js", 4 | "scripts": { 5 | "start": "node index.js" 6 | }, 7 | "dependencies": { 8 | "@bitwarden/sdk-client": "../sdk-client", 9 | "@bitwarden/sdk-wasm": "../wasm" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /languages/js/sdk-client/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | pkg 3 | -------------------------------------------------------------------------------- /languages/js/sdk-client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bitwarden/sdk-client", 3 | "exports": { 4 | ".": { 5 | "types": "./dist/src/lib.d.ts", 6 | "default": "./dist/src/lib.js" 7 | } 8 | }, 9 | "main": "dist/src/lib.js", 10 | "types": "dist/src/lib.d.ts", 11 | "scripts": { 12 | "build": "npm run clean && tsc", 13 | "clean": "rimraf dist" 14 | }, 15 | "devDependencies": { 16 | "@types/node": "^20.0.0", 17 | "rimraf": "^6.0.0", 18 | "typescript": "^5.0.3" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /languages/js/sdk-client/src/lib.ts: -------------------------------------------------------------------------------- 1 | export * from "./client"; 2 | export * from "./schemas"; 3 | -------------------------------------------------------------------------------- /languages/js/sdk-client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "module": "commonjs", 5 | "target": "es5", 6 | "moduleResolution": "node", 7 | "sourceMap": true, 8 | "composite": true, 9 | "declarationMap": true 10 | }, 11 | "include": ["./src/**/*"], 12 | } 13 | -------------------------------------------------------------------------------- /languages/js/wasm/.gitignore: -------------------------------------------------------------------------------- 1 | bitwarden_wasm_bg.js 2 | bitwarden_wasm_bg.wasm 3 | bitwarden_wasm_bg.wasm.d.ts 4 | bitwarden_wasm_bg.wasm.js 5 | bitwarden_wasm.d.ts 6 | bitwarden_wasm.js 7 | -------------------------------------------------------------------------------- /languages/js/wasm/index.js: -------------------------------------------------------------------------------- 1 | // https://stackoverflow.com/a/47880734 2 | const supported = (() => { 3 | try { 4 | if (typeof WebAssembly === "object" 5 | && typeof WebAssembly.instantiate === "function") { 6 | const module = new WebAssembly.Module(Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00)); 7 | if (module instanceof WebAssembly.Module) 8 | return new WebAssembly.Instance(module) instanceof WebAssembly.Instance; 9 | } 10 | } catch (e) { 11 | } 12 | return false; 13 | })(); 14 | 15 | let wasm; 16 | 17 | if (supported) { 18 | wasm = await import('./bitwarden_wasm_bg.wasm'); 19 | } else { 20 | wasm = await import('./bitwarden_wasm_bg.wasm.js'); 21 | } 22 | 23 | import { __wbg_set_wasm } from "./bitwarden_wasm_bg.js"; 24 | __wbg_set_wasm(wasm); 25 | export * from "./bitwarden_wasm_bg.js"; 26 | -------------------------------------------------------------------------------- /languages/js/wasm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bitwarden/sdk-wasm", 3 | "version": "0.1.0", 4 | "files": [ 5 | "bitwarden_wasm_bg.js", 6 | "bitwarden_wasm_bg.wasm", 7 | "bitwarden_wasm_bg.wasm.d.ts", 8 | "bitwarden_wasm_bg.wasm.js", 9 | "bitwarden_wasm.d.ts", 10 | "bitwarden_wasm.js", 11 | "index.js", 12 | "node/bitwarden_wasm_bg.wasm", 13 | "node/bitwarden_wasm_bg.wasm.d.ts", 14 | "node/bitwarden_wasm.d.ts", 15 | "node/bitwarden_wasm.js" 16 | ], 17 | "main": "node/bitwarden_wasm.js", 18 | "module": "index.js", 19 | "types": "bitwarden_wasm.d.ts", 20 | "scripts": {}, 21 | "sideEffects": [ 22 | "./bitwarden_wasm.js", 23 | "./snippets/*" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /languages/php/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | vendor 3 | src/lib/ 4 | src/Schemas/ 5 | -------------------------------------------------------------------------------- /languages/php/INSTALL.md: -------------------------------------------------------------------------------- 1 | # PHP Installation 2 | 3 | ## Introduction 4 | 5 | Composer is used to build the PHP Bitwarden client library. 6 | 7 | ## Prerequisites 8 | 9 | - PHP >= 8.0 10 | - FFI extension enabled in PHP configuration 11 | - Composer 12 | - Bitwarden SDK native library. 13 | - Expected in one of below locations, depending on the OS and architecture. 14 | The `src` is relative path to the [src](./src) directory. 15 | - Windows x86_64: `src\lib\windows-x64\bitwarden_c.dll` 16 | - Linux x86_64: `src/lib/linux-x64/libbitwarden_c.so` 17 | - macOS x86_64: `src/lib/macos-x64/libbitwarden_c.dylib` 18 | - macOS aarch64: `src/lib/macos-arm64/libbitwarden_c.dylib` 19 | - If you prefer to build the SDK yourself, see the [SDK README.md](../../README.md) for instructions. 20 | 21 | ## Build Commands 22 | 23 | ```shell 24 | composer install 25 | ``` 26 | 27 | ## Example 28 | 29 | ### macOS 30 | 31 | #### Install Prerequisites 32 | 33 | Use brew Composer and PHP 34 | 35 | ```shell 36 | brew install php 37 | brew install composer 38 | ``` 39 | 40 | #### Build Commands 41 | 42 | ```shell 43 | composer install 44 | ``` 45 | 46 | ## Example SDK Usage Project 47 | 48 | ```shell 49 | export ACCESS_TOKEN="" 50 | export STATE_FILE="" 51 | export ORGANIZATION_ID="" 52 | export API_URL="https://api.bitwarden.com" 53 | export IDENTITY_URL="https://identity.bitwarden.com" 54 | 55 | php example.php 56 | ``` 57 | -------------------------------------------------------------------------------- /languages/php/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bitwarden/sdk-secrets", 3 | "description": "PHP bindings for interacting with the Bitwarden Secrets Manager. This is a beta release and might be missing some functionality.", 4 | "type": "library", 5 | "keywords": ["bitwarden","sdk","password-manager"], 6 | "homepage": "https://github.com/bitwarden/sdk-sm", 7 | "version": "1.0.0", 8 | "require": { 9 | "php": "^8.0", 10 | "ext-ffi": "*" 11 | }, 12 | "autoload": { 13 | "psr-4": { 14 | "Bitwarden\\Sdk\\": "src/" 15 | }, 16 | "files": ["src/Schemas/Schemas.php"] 17 | }, 18 | "authors": [ 19 | { 20 | "name": "Bitwarden Inc." 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /languages/php/composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "1769eb8cdcb42d17f993aa0ef123895b", 8 | "packages": [], 9 | "packages-dev": [], 10 | "aliases": [], 11 | "minimum-stability": "stable", 12 | "stability-flags": [], 13 | "prefer-stable": false, 14 | "prefer-lowest": false, 15 | "platform": { 16 | "php": "^8.0", 17 | "ext-ffi": "*" 18 | }, 19 | "platform-dev": [], 20 | "plugin-api-version": "2.6.0" 21 | } 22 | -------------------------------------------------------------------------------- /languages/php/src/AuthClient.php: -------------------------------------------------------------------------------- 1 | commandRunner = $commandRunner; 16 | } 17 | 18 | /** 19 | * @throws Exception 20 | */ 21 | public function login_access_token(string $access_token, ?string $state_file): void 22 | { 23 | $access_token_request = new AccessTokenLoginRequest($access_token, $state_file); 24 | $command = new Command(passwordLogin: null, apiKeyLogin: null, loginAccessToken: $access_token_request, 25 | getUserApiKey: null, fingerprint: null, sync: null, secrets: null, projects: null, generators: null); 26 | try { 27 | $result = $this->commandRunner->run($command); 28 | if (!isset($result->authenticated) || !$result->authenticated) { 29 | throw new Exception("Unauthorized"); 30 | } 31 | } catch (Exception $exception) { 32 | throw new Exception("Authorization error: " . $exception->getMessage()); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /languages/php/src/BitwardenClient.php: -------------------------------------------------------------------------------- 1 | clientSettings = new ClientSettings(apiUrl: $bitwardenSettings->get_api_url(), 29 | deviceType: DeviceType::$SDK, identityUrl: $bitwardenSettings->get_identity_url(), 30 | userAgent: "Bitwarden PHP-SDK"); 31 | 32 | $this->bitwarden_lib = new BitwardenLib(); 33 | $this->bitwarden_lib->init($this->clientSettings); 34 | 35 | $this->commandRunner = new CommandRunner($this->bitwarden_lib); 36 | $this->projects = new ProjectsClient($this->commandRunner); 37 | $this->secrets = new SecretsClient($this->commandRunner); 38 | $this->auth = new AuthClient($this->commandRunner); 39 | } 40 | 41 | public function __destruct() 42 | { 43 | $this->bitwarden_lib->free_mem(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /languages/php/src/BitwardenSettings.php: -------------------------------------------------------------------------------- 1 | api_url = $api_url; 14 | $this->identity_url = $identity_url; 15 | } 16 | 17 | public function get_api_url(): ?string 18 | { 19 | return $this->api_url; 20 | } 21 | 22 | public function get_identity_url(): ?string 23 | { 24 | return $this->identity_url; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /languages/php/src/CommandRunner.php: -------------------------------------------------------------------------------- 1 | bitwardenLib = $bitwardenLib; 16 | } 17 | 18 | /** 19 | * @throws Exception 20 | */ 21 | public function run(Command $command): stdClass 22 | { 23 | $result = $this->bitwardenLib->run_command($command); 24 | if ($result->success) { 25 | return $result->data; 26 | } 27 | 28 | if (isset($result->errorMessage)) { 29 | throw new Exception($result->errorMessage); 30 | } 31 | throw new Exception("Unknown error occurred"); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /languages/python/.gitignore: -------------------------------------------------------------------------------- 1 | *.egg-info 2 | bitwarden_py*.so 3 | __pycache__ 4 | .venv 5 | -------------------------------------------------------------------------------- /languages/python/README.md: -------------------------------------------------------------------------------- 1 | # Build locally 2 | ## Requirements 3 | 4 | - Python 3 5 | - Rust 6 | - `maturin` (install with `pip install maturin`) 7 | - `npm` 8 | 9 | ## Build 10 | 11 | ```bash 12 | npm install 13 | npm run schemas # generate schemas.py 14 | 15 | cd languages/python/ 16 | ``` 17 | 18 | You will need to build and run the script using a virtual environment. 19 | This will be slightly different depending on the OS you are using: 20 | 21 | ```bash 22 | # --- Linux/macOS --- 23 | python3 -m venv .venv 24 | source .venv/bin/activate 25 | 26 | # --- Windows --- 27 | python -m venv venv 28 | 29 | venv\Scripts\activate.bat # cmd.exe 30 | venv\Scripts\Activate.ps1 # Powershell 31 | ``` 32 | 33 | ## Run 34 | 35 | ```bash 36 | maturin develop 37 | python3 ./example.py 38 | 39 | deactivate # run this to close the virtual session 40 | ``` 41 | 42 | You can now import `BitwardenClient` in your Python code with: 43 | ```python 44 | from bitwarden_sdk import BitwardenClient 45 | ``` 46 | 47 | # Use without building locally 48 | 49 | ```bash 50 | pip install bitwarden-sdk 51 | ``` 52 | 53 | # Run 54 | 55 | Set the `ORGANIZATION_ID` and `ACCESS_TOKEN` environment variables to your organization ID and access token, respectively. 56 | 57 | ```bash 58 | python3 ./example.py 59 | ``` 60 | -------------------------------------------------------------------------------- /languages/python/bitwarden_sdk/__init__.py: -------------------------------------------------------------------------------- 1 | """The official Bitwarden client library for Python.""" 2 | 3 | __version__ = "1.0.0" 4 | 5 | from .bitwarden_client import * 6 | from .schemas import * 7 | 8 | __doc__ = bitwarden_client.__doc__ 9 | if hasattr(bitwarden_client, "__all__"): 10 | __all__ = bitwarden_client.__all__ 11 | 12 | if hasattr(schemas, "__all__"): 13 | __all__ += schemas.__all__ 14 | -------------------------------------------------------------------------------- /languages/python/example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import logging 3 | import os 4 | from datetime import datetime, timezone 5 | 6 | from bitwarden_sdk import BitwardenClient, DeviceType, client_settings_from_dict 7 | 8 | # Create the BitwardenClient, which is used to interact with the SDK 9 | client = BitwardenClient( 10 | client_settings_from_dict( 11 | { 12 | "apiUrl": os.getenv("API_URL", "http://localhost:4000"), 13 | "deviceType": DeviceType.SDK, 14 | "identityUrl": os.getenv("IDENTITY_URL", "http://localhost:33656"), 15 | "userAgent": "Python", 16 | } 17 | ) 18 | ) 19 | 20 | # Add some logging & set the org id 21 | logging.basicConfig(level=logging.DEBUG) 22 | organization_id = os.getenv("ORGANIZATION_ID") 23 | 24 | # Set the state file location 25 | # Note: the path must exist, the file will be created & managed by the sdk 26 | state_path = os.getenv("STATE_FILE") 27 | 28 | # Attempt to authenticate with the Secrets Manager Access Token 29 | client.auth().login_access_token(os.getenv("ACCESS_TOKEN"), state_path) 30 | 31 | # -- Example Project Commands -- 32 | 33 | project = client.projects().create(organization_id, "ProjectName") 34 | project2 = client.projects().create(organization_id, "AnotherProject") 35 | updated_project = client.projects().update( 36 | organization_id, project.data.id, "Cool New Project Name" 37 | ) 38 | get_that_project = client.projects().get(project.data.id) 39 | 40 | input("Press Enter to delete the project...") 41 | client.projects().delete([project.data.id]) 42 | 43 | print(client.projects().list(organization_id)) 44 | 45 | # -- Example Secret Commands -- 46 | 47 | if client.secrets().sync(organization_id, None).data.has_changes is True: 48 | print("There are changes to sync") 49 | else: 50 | print("No changes to sync") 51 | 52 | last_synced_date = datetime.now(tz=timezone.utc) 53 | print(client.secrets().sync(organization_id, last_synced_date)) 54 | 55 | secret = client.secrets().create( 56 | organization_id, 57 | "TEST_SECRET", 58 | "This is a test secret", 59 | "Secret1234!", 60 | [project2.data.id], 61 | ) 62 | secret2 = client.secrets().create( 63 | organization_id, 64 | "ANOTHER_SECRET", 65 | "Secret1234!", 66 | None, 67 | [project2.data.id], 68 | ) 69 | secret_updated = client.secrets().update( 70 | organization_id, 71 | secret.data.id, 72 | "TEST_SECRET_UPDATED", 73 | "This as an updated test secret", 74 | "Secret1234!_updated", 75 | [project2.data.id], 76 | ) 77 | secrets_retrieved = client.secrets().get_by_ids([secret.data.id, secret2.data.id]) 78 | 79 | # cleanup 80 | input("Press Enter to cleanup secrets and projects...") 81 | client.secrets().delete([secret.id for secret in secrets_retrieved.data.data]) 82 | 83 | client.projects().delete([project2.data.id]) 84 | -------------------------------------------------------------------------------- /languages/python/openapitools.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json", 3 | "spaces": 2, 4 | "generator-cli": { 5 | "version": "6.0.0" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /languages/python/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | build-backend = "maturin" 3 | requires = ["maturin>=1.0,<2.0", "setuptools_rust>=1.8.1"] 4 | 5 | [project] 6 | authors = [{ name = "Bitwarden", email = "support@bitwarden.com" }] 7 | classifiers = [ 8 | "Development Status :: 4 - Beta", 9 | "Intended Audience :: Developers", 10 | "License :: Other/Proprietary License", 11 | "Programming Language :: Python :: 3 :: Only", 12 | "Programming Language :: Rust", 13 | "Topic :: Security", 14 | ] 15 | dependencies = ["dateutils >= 0.6.6"] 16 | description = "A Bitwarden Client for python" 17 | name = "bitwarden_sdk" 18 | readme = "README.md" 19 | requires-python = ">=3.0" 20 | version = "1.0.0" 21 | 22 | [tool.maturin] 23 | bindings = "pyo3" 24 | compatibility = "2_28" 25 | include = [ 26 | { path = "bitwarden_sdk/*.py", format = ["sdist", "wheel"] } 27 | ] 28 | manifest-path = "../../crates/bitwarden-py/Cargo.toml" 29 | python-packages = ["bitwarden_sdk"] 30 | -------------------------------------------------------------------------------- /languages/ruby/.gitignore: -------------------------------------------------------------------------------- 1 | *.lock 2 | *.gem 3 | bitwarden_sdk_secrets/lib/schemas.rb 4 | bitwarden_sdk_secrets/pkg 5 | -------------------------------------------------------------------------------- /languages/ruby/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [Unreleased] 2 | 3 | ## [0.1.0] - 2024-02-23 4 | 5 | - Initial release 6 | 7 | -------------------------------------------------------------------------------- /languages/ruby/bitwarden_sdk_secrets/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | # Specify your gem's dependencies in exmp.gemspec 6 | gemspec 7 | 8 | gem "rake", "~> 13.0" 9 | gem "rspec", "~> 3.0" 10 | gem "rubocop", "~> 1.21" 11 | -------------------------------------------------------------------------------- /languages/ruby/bitwarden_sdk_secrets/Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "bundler/gem_tasks" 4 | require "rubocop/rake_task" 5 | require 'rspec/core/rake_task' 6 | 7 | RSpec::Core::RakeTask.new 8 | 9 | RuboCop::RakeTask.new 10 | 11 | task default: :rubocop 12 | -------------------------------------------------------------------------------- /languages/ruby/bitwarden_sdk_secrets/bitwarden-sdk-secrets.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'lib/version' 4 | 5 | Gem::Specification.new do |spec| 6 | spec.name = 'bitwarden-sdk-secrets' 7 | spec.version = BitwardenSDKSecrets::VERSION 8 | spec.authors = ['Bitwarden Inc.'] 9 | spec.email = ['hello@bitwarden_sdk.com'] 10 | 11 | spec.summary = 'Bitwarden Secrets Manager SDK.' 12 | spec.description = 'Ruby wrapper for Bitwarden secrets manager SDK.' 13 | spec.homepage = 'https://bitwarden.com/products/secrets-manager/' 14 | spec.required_ruby_version = '>= 3.0.0' 15 | 16 | spec.metadata['homepage_uri'] = spec.homepage 17 | spec.metadata['source_code_uri'] = 'https://github.com/bitwarden/sdk-sm' 18 | spec.metadata['changelog_uri'] = 'https://github.com/bitwarden/sdk-sm/blob/main/languages/ruby/CHANGELOG.md' 19 | 20 | # Specify which files should be added to the gem when it is released. 21 | # The `git ls-files -z` loads the files in the RubyGem that have been added into git. 22 | spec.files = Dir.chdir(__dir__) do 23 | `git ls-files -z`.split("\x0").reject do |f| 24 | (File.expand_path(f) == __FILE__) || f.start_with?(*%w[bin/ test/ spec/ features/ .git Gemfile]) 25 | end 26 | end 27 | 28 | spec.files += Dir.glob('lib/linux-x64/**/*') 29 | spec.files += Dir.glob('lib/macos-x64/**/*') 30 | spec.files += Dir.glob('lib/windows-x64/**/*') 31 | spec.files += Dir.glob('lib/macos-arm64/**/*') 32 | spec.files += Dir.glob('lib/schemas.rb') 33 | 34 | spec.bindir = 'exe' 35 | spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } 36 | spec.require_paths = ['lib'] 37 | 38 | # Uncomment to register a new dependency of your gem 39 | # spec.add_dependency "example-gem", "~> 1.0" 40 | spec.add_dependency 'dry-struct', '~> 1.6' 41 | spec.add_dependency 'dry-types', '~> 1.7' 42 | spec.add_dependency 'ffi', '~> 1.15' 43 | spec.add_dependency 'json', '~> 2.6' 44 | spec.add_dependency 'rake', '~> 13.0' 45 | spec.add_dependency 'rubocop', '~> 1.21' 46 | 47 | end 48 | -------------------------------------------------------------------------------- /languages/ruby/bitwarden_sdk_secrets/lib/auth.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require_relative 'bitwarden_error' 3 | 4 | module BitwardenSDKSecrets 5 | class AuthClient 6 | def initialize(command_runner) 7 | @command_runner = command_runner 8 | end 9 | 10 | def login_access_token(access_token, state_file = nil) 11 | access_token_request = AccessTokenLoginRequest.new(access_token: access_token, state_file: state_file) 12 | @command_runner.run(SelectiveCommand.new(login_access_token: access_token_request)) 13 | nil 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /languages/ruby/bitwarden_sdk_secrets/lib/bitwarden-sdk-secrets.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'json' 4 | require 'dry-types' 5 | 6 | require_relative 'schemas' 7 | require_relative 'extended_schemas/schemas' 8 | require_relative 'command_runner' 9 | require_relative 'bitwarden_lib' 10 | require_relative 'bitwarden_error' 11 | require_relative 'projects' 12 | require_relative 'secrets' 13 | require_relative 'auth' 14 | 15 | module BitwardenSDKSecrets 16 | class BitwardenSettings 17 | attr_accessor :api_url, :identity_url 18 | 19 | def initialize(api_url, identity_url) 20 | # if api_url.nil? || identity_url.nil? 21 | # raise ArgumentError, "api_url and identity_url cannot be nil" 22 | # end 23 | 24 | @api_url = api_url 25 | @identity_url = identity_url 26 | end 27 | end 28 | 29 | class BitwardenClient 30 | attr_reader :bitwarden, :projects, :secrets, :auth 31 | 32 | def initialize(bitwarden_settings) 33 | client_settings = ClientSettings.new( 34 | api_url: bitwarden_settings.api_url, 35 | identity_url: bitwarden_settings.identity_url, 36 | user_agent: 'Bitwarden RUBY-SDK', 37 | device_type: nil 38 | ) 39 | 40 | @bitwarden = BitwardenLib 41 | @handle = @bitwarden.init(client_settings.to_dynamic.compact.to_json) 42 | @command_runner = CommandRunner.new(@bitwarden, @handle) 43 | @projects = ProjectsClient.new(@command_runner) 44 | @secrets = SecretsClient.new(@command_runner) 45 | @auth = AuthClient.new(@command_runner) 46 | end 47 | 48 | def free_mem 49 | @bitwarden.free_mem(@handle) 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /languages/ruby/bitwarden_sdk_secrets/lib/bitwarden_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module BitwardenSDKSecrets 4 | class BitwardenError < StandardError 5 | def initialize(message = 'Error getting response') 6 | super(message) 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /languages/ruby/bitwarden_sdk_secrets/lib/bitwarden_lib.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'ffi' 4 | 5 | module BitwardenSDKSecrets 6 | module BitwardenLib 7 | extend FFI::Library 8 | 9 | def self.mac_with_intel? 10 | `uname -m`.strip == 'x86_64' 11 | end 12 | 13 | ffi_lib case RUBY_PLATFORM 14 | when /darwin/ 15 | local_file = if mac_with_intel? 16 | File.expand_path('macos-x64/libbitwarden_c.dylib', __dir__) 17 | else 18 | File.expand_path('macos-arm64/libbitwarden_c.dylib', __dir__) 19 | end 20 | File.exist?(local_file) ? local_file : File.expand_path('../../../../target/debug/libbitwarden_c.dylib', __dir__) 21 | when /linux/ 22 | local_file = File.expand_path('linux-x64/libbitwarden_c.so', __dir__) 23 | File.exist?(local_file) ? local_file : File.expand_path('../../../../target/debug/libbitwarden_c.so', __dir__) 24 | when /mswin|mingw/ 25 | local_file = File.expand_path('windows-x64/bitwarden_c.dll', __dir__) 26 | File.exist?(local_file) ? local_file : File.expand_path('../../../../target/debug/bitwarden_c.dll', __dir__) 27 | else 28 | raise "Unsupported platform: #{RUBY_PLATFORM}" 29 | end 30 | 31 | attach_function :init, [:string], :pointer 32 | attach_function :run_command, %i[string pointer], :string 33 | attach_function :free_mem, [:pointer], :void 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /languages/ruby/bitwarden_sdk_secrets/lib/command_runner.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module BitwardenSDKSecrets 4 | class CommandRunner 5 | def initialize(bitwarden_sdk, handle) 6 | @bitwarden_sdk = bitwarden_sdk 7 | @handle = handle 8 | end 9 | 10 | # @param [Dry-Struct] cmd 11 | def run(cmd) 12 | @bitwarden_sdk.run_command(cmd.to_json, @handle) 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /languages/ruby/bitwarden_sdk_secrets/lib/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module BitwardenSDKSecrets 4 | VERSION = '1.0.0' 5 | end 6 | -------------------------------------------------------------------------------- /languages/ruby/bitwarden_sdk_secrets/sig/auth.rbs: -------------------------------------------------------------------------------- 1 | module BitwardenSDKSecrets 2 | class AuthClient 3 | @command_runner: untyped 4 | 5 | def initialize: (untyped command_runner) -> void 6 | 7 | def login_access_token: (untyped access_token, ?untyped? state_file) -> nil 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /languages/ruby/bitwarden_sdk_secrets/sig/bitwarden-sdk-secrets.rbs: -------------------------------------------------------------------------------- 1 | module BitwardenSDKSecrets 2 | class BitwardenSettings 3 | @api_url: untyped 4 | 5 | @identity_url: untyped 6 | 7 | attr_accessor api_url: untyped 8 | 9 | attr_accessor identity_url: untyped 10 | 11 | def initialize: (untyped api_url, untyped identity_url) -> void 12 | end 13 | 14 | class BitwardenClient 15 | @bitwarden: untyped 16 | 17 | @handle: untyped 18 | 19 | @command_runner: untyped 20 | 21 | @projects: untyped 22 | 23 | @secrets: untyped 24 | 25 | @auth: untyped 26 | 27 | attr_reader bitwarden: untyped 28 | 29 | attr_reader projects: untyped 30 | 31 | attr_reader secrets: untyped 32 | 33 | attr_reader auth: untyped 34 | 35 | def initialize: (untyped bitwarden_settings) -> void 36 | 37 | def free_mem: () -> untyped 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /languages/ruby/bitwarden_sdk_secrets/sig/bitwarden_error.rbs: -------------------------------------------------------------------------------- 1 | module BitwardenSDKSecrets 2 | class BitwardenError < StandardError 3 | def initialize: (?::String message) -> void 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /languages/ruby/bitwarden_sdk_secrets/sig/bitwarden_lib.rbs: -------------------------------------------------------------------------------- 1 | module BitwardenSDKSecrets 2 | module BitwardenLib 3 | extend FFI::Library 4 | 5 | def self.mac_with_intel?: () -> untyped 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /languages/ruby/bitwarden_sdk_secrets/sig/command_runner.rbs: -------------------------------------------------------------------------------- 1 | module BitwardenSDKSecrets 2 | class CommandRunner 3 | @bitwarden_sdk: untyped 4 | 5 | @handle: untyped 6 | 7 | def initialize: (untyped bitwarden_sdk, untyped handle) -> void 8 | 9 | # @param [Dry-Struct] cmd 10 | def run: (untyped cmd) -> untyped 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /languages/ruby/bitwarden_sdk_secrets/sig/projects.rbs: -------------------------------------------------------------------------------- 1 | module BitwardenSDKSecrets 2 | class ProjectsClient 3 | @command_runner: untyped 4 | 5 | def initialize: (untyped command_runner) -> void 6 | 7 | def create: (untyped organization_id, untyped project_name) -> untyped 8 | 9 | def get: (untyped project_id) -> untyped 10 | 11 | def list: (untyped organization_id) -> untyped 12 | 13 | def update: (untyped organization_id, untyped id, untyped project_put_request_name) -> untyped 14 | 15 | def delete: (untyped ids) -> untyped 16 | 17 | private 18 | 19 | def error_response: (untyped response) -> untyped 20 | 21 | def create_command: (untyped commands) -> untyped 22 | 23 | def parse_response: (untyped command) -> untyped 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /languages/ruby/bitwarden_sdk_secrets/sig/secrets.rbs: -------------------------------------------------------------------------------- 1 | module BitwardenSDKSecrets 2 | class SecretsClient 3 | @command_runner: untyped 4 | 5 | def initialize: (untyped command_runner) -> void 6 | 7 | def get: (untyped id) -> untyped 8 | 9 | def get_by_ids: (untyped ids) -> untyped 10 | 11 | def sync: (untyped organization_id, untyped last_synced_date) -> untyped 12 | 13 | def create: (untyped organization_id, untyped key, untyped value, untyped note, untyped project_ids) -> untyped 14 | 15 | def list: (untyped organization_id) -> untyped 16 | 17 | def update: (untyped organization_id, untyped id, untyped key, untyped value, untyped note, untyped project_ids) -> untyped 18 | 19 | def delete: (untyped ids) -> untyped 20 | 21 | private 22 | 23 | def error_response: (untyped response) -> (untyped | nil | untyped) 24 | 25 | def create_command: (untyped commands) -> untyped 26 | 27 | def run_command: (untyped command) -> untyped 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /languages/ruby/bitwarden_sdk_secrets/sig/version.rbs: -------------------------------------------------------------------------------- 1 | module BitwardenSDKSecrets 2 | VERSION: "0.2.0" 3 | end 4 | -------------------------------------------------------------------------------- /languages/ruby/bitwarden_sdk_secrets/spec/settings_spec.rb: -------------------------------------------------------------------------------- 1 | require 'schemas' 2 | require 'extended_schemas/schemas' 3 | 4 | describe ClientSettings do 5 | it "test" do 6 | client_settings = ClientSettings.new( 7 | api_url: nil, 8 | identity_url: nil, 9 | user_agent: 'Bitwarden RUBY-SDK', 10 | device_type: nil 11 | ) 12 | 13 | expect(client_settings.to_dynamic.compact.to_json).to eq('{"userAgent":"Bitwarden RUBY-SDK"}') 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /languages/ruby/examples/example.rb: -------------------------------------------------------------------------------- 1 | # NOTE - for example purpose only - import gem instead 2 | require 'bitwarden-sdk-secrets' 3 | 4 | token = ENV['ACCESS_TOKEN'] 5 | organization_id = ENV['ORGANIZATION_ID'] 6 | state_file = ENV['STATE_FILE'] 7 | 8 | # Configuring the URLS is optional, set them to nil to use the default values 9 | api_url = ENV['API_URL'] 10 | identity_url = ENV['IDENTITY_URL'] 11 | 12 | bitwarden_settings = BitwardenSDKSecrets::BitwardenSettings.new(api_url, identity_url) 13 | 14 | bw_client = BitwardenSDKSecrets::BitwardenClient.new(bitwarden_settings) 15 | response = bw_client.auth.login_access_token(token, state_file) 16 | puts response 17 | 18 | # CREATE project 19 | project_name = 'Test project 1' 20 | response = bw_client.projects.create(organization_id, project_name) 21 | puts response 22 | project_id = response['id'] 23 | 24 | # GET project 25 | response = bw_client.projects.get(project_id) 26 | puts response 27 | 28 | # LIST projects 29 | response = bw_client.projects.list(organization_id) 30 | puts response 31 | 32 | # UPDATE projects 33 | name = 'Updated test project 1' 34 | response = bw_client.projects.update(organization_id, project_id, name) 35 | puts response 36 | 37 | # CREATE secret 38 | key = 'AWS-SES' 39 | note = 'Private account' 40 | value = '8t27.dfj;' 41 | response = bw_client.secrets.create(organization_id, key, value, note, [project_id]) 42 | puts response 43 | secret_id = response['id'] 44 | 45 | # GET secret 46 | response = bw_client.secrets.get(secret_id) 47 | puts response 48 | 49 | # GET secret by ids 50 | response = bw_client.secrets.get_by_ids([secret_id]) 51 | puts response 52 | 53 | # LIST secrets 54 | response = bw_client.secrets.list(organization_id) 55 | puts response 56 | 57 | # SYNC secrets 58 | response = bw_client.secrets.sync(organization_id, nil) 59 | last_synced_date = Time.now.utc.strftime('%Y-%m-%dT%H:%M:%S.%6NZ') 60 | puts response 61 | 62 | response = bw_client.secrets.sync(organization_id, last_synced_date) 63 | puts response 64 | 65 | # UPDATE secret 66 | note = 'updated password' 67 | value = '7I.ert10AjK' 68 | response = bw_client.secrets.update(organization_id, secret_id, key, value, note, [project_id]) 69 | puts response 70 | 71 | # DELETE secret 72 | response = bw_client.secrets.delete([secret_id]) 73 | puts response 74 | 75 | # DELETE project 76 | response = bw_client.projects.delete([project_id]) 77 | puts response 78 | -------------------------------------------------------------------------------- /languages/ruby/gen_ruby_typedefs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # shellcheck disable=SC3044,SC3020 3 | 4 | # bail if rbs is not installed 5 | if ! command -v rbs &>/dev/null; then 6 | echo "rbs could not be found" 7 | exit 8 | fi 9 | 10 | # use consistent repository root to avoid relative paths 11 | REPO_ROOT="$(git rev-parse --show-toplevel)" 12 | pushd "$REPO_ROOT"/languages/ruby || exit 13 | 14 | # delete existing typedefs 15 | rm -rf bitwarden_sdk_secrets/sig/* 16 | mkdir -p bitwarden_sdk_secrets/sig 17 | 18 | # generate typedefs 19 | RUBY_LIB_FILES="$(find bitwarden_sdk_secrets/lib -name "*.rb")" 20 | 21 | for file in $RUBY_LIB_FILES; do 22 | rbs prototype rb "$file" >bitwarden_sdk_secrets/sig/"$(basename "$file" .rb).rbs" 23 | rm -f bitwarden_sdk_secrets/sig/schemas.rbs 24 | done 25 | 26 | popd || exit 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bitwarden/sdk", 3 | "version": "0.0.0", 4 | "description": "", 5 | "homepage": "https://github.com/bitwarden/sdk-sm#readme", 6 | "bugs": { 7 | "url": "https://github.com/bitwarden/sdk-sm/issues" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/bitwarden/sdk-sm.git" 12 | }, 13 | "license": "SEE LICENSE IN LICENSE", 14 | "author": "Bitwarden Inc. (https://bitwarden.com)", 15 | "main": "index.js", 16 | "scripts": { 17 | "lint": "prettier --check .", 18 | "prettier": "prettier --write .", 19 | "schemas": "rimraf ./support/schemas && cargo run --bin sdk-schemas --all-features && ts-node ./support/scripts/schemas.ts", 20 | "test": "echo \"Error: no test specified\" && exit 1" 21 | }, 22 | "devDependencies": { 23 | "prettier": "3.3.3", 24 | "quicktype-core": "23.0.170", 25 | "rimraf": "6.0.1", 26 | "ts-node": "10.9.2", 27 | "typescript": "5.5.4" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # Wrap comments and increase the width of comments to 100 2 | comment_width = 100 3 | wrap_comments = true 4 | 5 | # Sort and group imports 6 | group_imports = "StdExternalCrate" 7 | imports_granularity = "Crate" 8 | -------------------------------------------------------------------------------- /sig/bitwarden_sdk/bitwarden_client.rbs: -------------------------------------------------------------------------------- 1 | module BitwardenSDK 2 | class BitwardenClient 3 | attr_reader project_client: ProjectsClient 4 | end 5 | end 6 | --------------------------------------------------------------------------------