diff --git a/README.md b/README.md index ed3093448..497af6547 100644 --- a/README.md +++ b/README.md @@ -20,10 +20,21 @@ are currently open as well as what it's like to work at Bitwarden. ## Getting Started +### Linux / Mac / Windows + ```bash cargo build ``` +### Windows on ARM + +To build, you will need the following in your PATH: + +- [Python](https://www.python.org) +- [Clang](https://clang.llvm.org) + - We recommend installing this via the + [Visual Studio Build Tools](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2022) + ## Crates The project is structured as a monorepo using cargo workspaces. Some of the more noteworthy crates diff --git a/crates/bitwarden-napi/README.md b/crates/bitwarden-napi/README.md index d9e3e7f27..e9c3d0a71 100644 --- a/crates/bitwarden-napi/README.md +++ b/crates/bitwarden-napi/README.md @@ -21,10 +21,7 @@ const accessToken = "-- REDACTED --"; const client = new BitwardenClient(settings, LogLevel.Info); // Authenticating using a machine account access token -const result = await client.loginWithAccessToken(accessToken); -if (!result.success) { - throw Error("Authentication failed"); -} +await client.accessTokenLogin(accessToken); // List secrets const secrets = await client.secrets().list(); diff --git a/crates/bitwarden-napi/binding.d.ts b/crates/bitwarden-napi/binding.d.ts index 50ec34289..174d33d92 100644 --- a/crates/bitwarden-napi/binding.d.ts +++ b/crates/bitwarden-napi/binding.d.ts @@ -10,7 +10,7 @@ export const enum LogLevel { Warn = 3, Error = 4, } -export class BitwardenClient { +export declare class BitwardenClient { constructor(settingsInput?: string | undefined | null, logLevel?: LogLevel | undefined | null); runCommand(commandInput: string): Promise; } diff --git a/crates/bitwarden-napi/binding.js b/crates/bitwarden-napi/binding.js index c3cf32037..3da97e8f9 100644 --- a/crates/bitwarden-napi/binding.js +++ b/crates/bitwarden-napi/binding.js @@ -1,4 +1,10 @@ -const { existsSync, readFileSync } = require("fs"); +/* tslint:disable */ +/* eslint-disable */ +/* prettier-ignore */ + +/* auto-generated by NAPI-RS */ + +const { existsSync, readFileSync } = require('fs') const { join } = require("path"); const { platform, arch } = process; @@ -11,7 +17,8 @@ function isMusl() { // For Node 10 if (!process.report || typeof process.report.getReport !== "function") { try { - return readFileSync("/usr/bin/ldd", "utf8").includes("musl"); + const lddPath = require("child_process").execSync("which ldd").toString().trim(); + return readFileSync(lddPath, "utf8").includes("musl"); } catch (e) { return true; } @@ -95,6 +102,15 @@ switch (platform) { } break; case "darwin": + localFileExisted = existsSync(join(__dirname, "sdk-napi.darwin-universal.node")); + try { + if (localFileExisted) { + nativeBinding = require("./sdk-napi.darwin-universal.node"); + } else { + nativeBinding = require("@bitwarden/sdk-napi-darwin-universal"); + } + break; + } catch {} switch (arch) { case "x64": localFileExisted = existsSync(join(__dirname, "sdk-napi.darwin-x64.node")); @@ -192,12 +208,62 @@ switch (platform) { } break; case "arm": - localFileExisted = existsSync(join(__dirname, "sdk-napi.linux-arm-gnueabihf.node")); + if (isMusl()) { + localFileExisted = existsSync(join(__dirname, "sdk-napi.linux-arm-musleabihf.node")); + try { + if (localFileExisted) { + nativeBinding = require("./sdk-napi.linux-arm-musleabihf.node"); + } else { + nativeBinding = require("@bitwarden/sdk-napi-linux-arm-musleabihf"); + } + } catch (e) { + loadError = e; + } + } else { + localFileExisted = existsSync(join(__dirname, "sdk-napi.linux-arm-gnueabihf.node")); + try { + if (localFileExisted) { + nativeBinding = require("./sdk-napi.linux-arm-gnueabihf.node"); + } else { + nativeBinding = require("@bitwarden/sdk-napi-linux-arm-gnueabihf"); + } + } catch (e) { + loadError = e; + } + } + break; + case "riscv64": + if (isMusl()) { + localFileExisted = existsSync(join(__dirname, "sdk-napi.linux-riscv64-musl.node")); + try { + if (localFileExisted) { + nativeBinding = require("./sdk-napi.linux-riscv64-musl.node"); + } else { + nativeBinding = require("@bitwarden/sdk-napi-linux-riscv64-musl"); + } + } catch (e) { + loadError = e; + } + } else { + localFileExisted = existsSync(join(__dirname, "sdk-napi.linux-riscv64-gnu.node")); + try { + if (localFileExisted) { + nativeBinding = require("./sdk-napi.linux-riscv64-gnu.node"); + } else { + nativeBinding = require("@bitwarden/sdk-napi-linux-riscv64-gnu"); + } + } catch (e) { + loadError = e; + } + } + break; + case "s390x": + localFileExisted = existsSync(join(__dirname, "sdk-napi.linux-s390x-gnu.node")); try { if (localFileExisted) { - nativeBinding = require("./sdk-napi.linux-arm-gnueabihf.node"); + nativeBinding = require("./sdk-napi.linux-s390x-gnu.node"); } else { - nativeBinding = require("@bitwarden/sdk-napi-linux-arm-gnueabihf"); + nativeBinding = require("@bitwarden/sdk-napi-linux-s390x-gnu"); } } catch (e) { loadError = e; diff --git a/crates/bitwarden-napi/package-lock.json b/crates/bitwarden-napi/package-lock.json index 641733b1a..8082a7a80 100644 --- a/crates/bitwarden-napi/package-lock.json +++ b/crates/bitwarden-napi/package-lock.json @@ -9,55 +9,13 @@ "version": "0.3.1", "license": "SEE LICENSE IN LICENSE", "devDependencies": { - "@napi-rs/cli": "^2.13.2", - "ts-node": "10.9.2", - "typescript": "^5.0.0" + "@napi-rs/cli": "2.18.4", + "typescript": "5.5.4" }, "engines": { "node": ">= 10" } }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, "node_modules/@napi-rs/cli": { "version": "2.18.4", "resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-2.18.4.tgz", @@ -75,146 +33,6 @@ "url": "https://github.com/sponsors/Brooooooklyn" } }, - "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.0.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.0.3.tgz", - "integrity": "sha512-/e0NZtK2gs6Vk2DoyrXSZZ4AlamqTkx0CcKx1Aq8/P4ITlRgU9OtVf5fl+LXkWWJce1M89pkSlH6lJJEnK7bQA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "undici-types": "~6.11.1" - } - }, - "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.3", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", - "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, - "license": "MIT" - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "license": "ISC" - }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, "node_modules/typescript": { "version": "5.5.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", @@ -228,31 +46,6 @@ "engines": { "node": ">=14.17" } - }, - "node_modules/undici-types": { - "version": "6.11.1", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.11.1.tgz", - "integrity": "sha512-mIDEX2ek50x0OlRgxryxsenE5XaQD4on5U2inY7RApK3SOJpofyw7uW2AyfMKkhAxXIceo2DeWGVGwyvng1GNQ==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true, - "license": "MIT" - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } } } } diff --git a/crates/bitwarden-napi/package.json b/crates/bitwarden-napi/package.json index 509cf1763..9f1048d18 100644 --- a/crates/bitwarden-napi/package.json +++ b/crates/bitwarden-napi/package.json @@ -29,9 +29,8 @@ "version": "napi version" }, "devDependencies": { - "@napi-rs/cli": "^2.13.2", - "ts-node": "10.9.2", - "typescript": "^5.0.0" + "@napi-rs/cli": "2.18.4", + "typescript": "5.5.4" }, "engines": { "node": ">= 10" diff --git a/crates/bitwarden-napi/src-ts/bitwarden_client/index.ts b/crates/bitwarden-napi/src-ts/bitwarden_client/index.ts index eefbb1204..52a53ef4f 100644 --- a/crates/bitwarden-napi/src-ts/bitwarden_client/index.ts +++ b/crates/bitwarden-napi/src-ts/bitwarden_client/index.ts @@ -1,15 +1,34 @@ import * as rust from "../../binding"; import { LogLevel } from "../../binding"; import { - ClientSettings, Convert, - ResponseForAPIKeyLoginResponse, - ResponseForSecretIdentifiersResponse, - ResponseForSecretResponse, - ResponseForSecretsDeleteResponse, - ResponseForSecretsResponse, + ClientSettings, + ProjectResponse, + ProjectsDeleteResponse, + ProjectsResponse, + SecretIdentifiersResponse, + SecretResponse, + SecretsDeleteResponse, + SecretsResponse, + SecretsSyncResponse, } from "./schemas"; +function handleResponse(response: { + success: boolean; + errorMessage?: string | null; + data?: T | null; +}): T { + if (!response.success) { + throw new Error(response.errorMessage || ""); + } + + if (response.data === null) { + throw new Error(response.errorMessage || "SDK response data is null"); + } + + return response.data as T; +} + export class BitwardenClient { client: rust.BitwardenClient; @@ -18,34 +37,26 @@ export class BitwardenClient { this.client = new rust.BitwardenClient(settingsJson, loggingLevel ?? LogLevel.Info); } - async loginWithAccessToken(accessToken: string): Promise { - const commandInput = Convert.commandToJson({ - accessTokenLogin: { - accessToken: accessToken, - }, - }); - const response = await this.client.runCommand(commandInput); - - return Convert.toResponseForAPIKeyLoginResponse(response); - } - - /* - async sync(excludeSubdomains = false): Promise { + async accessTokenLogin(accessToken: string, stateFile?: string): Promise { const response = await this.client.runCommand( Convert.commandToJson({ - sync: { - excludeSubdomains, + accessTokenLogin: { + accessToken, + stateFile, }, - }) + }), ); - return Convert.toResponseForSyncResponse(response); + handleResponse(Convert.toResponseForAccessTokenLoginResponse(response)); } - */ secrets(): SecretsClient { return new SecretsClient(this.client); } + + projects(): ProjectsClient { + return new ProjectsClient(this.client); + } } export class SecretsClient { @@ -55,7 +66,7 @@ export class SecretsClient { this.client = client; } - async get(id: string): Promise { + async get(id: string): Promise { const response = await this.client.runCommand( Convert.commandToJson({ secrets: { @@ -64,10 +75,10 @@ export class SecretsClient { }), ); - return Convert.toResponseForSecretResponse(response); + return handleResponse(Convert.toResponseForSecretResponse(response)); } - async getByIds(ids: string[]): Promise { + async getByIds(ids: string[]): Promise { const response = await this.client.runCommand( Convert.commandToJson({ secrets: { @@ -76,27 +87,28 @@ export class SecretsClient { }), ); - return Convert.toResponseForSecretsResponse(response); + return handleResponse(Convert.toResponseForSecretsResponse(response)); } async create( key: string, + value: string, note: string, + projectIds: string[], organizationId: string, - value: string, - ): Promise { + ): Promise { const response = await this.client.runCommand( Convert.commandToJson({ secrets: { - create: { key, note, organizationId, value }, + create: { key, value, note, projectIds, organizationId }, }, }), ); - return Convert.toResponseForSecretResponse(response); + return handleResponse(Convert.toResponseForSecretResponse(response)); } - async list(organizationId: string): Promise { + async list(organizationId: string): Promise { const response = await this.client.runCommand( Convert.commandToJson({ secrets: { @@ -105,28 +117,29 @@ export class SecretsClient { }), ); - return Convert.toResponseForSecretIdentifiersResponse(response); + return handleResponse(Convert.toResponseForSecretIdentifiersResponse(response)); } async update( id: string, key: string, + value: string, note: string, + projectIds: string[], organizationId: string, - value: string, - ): Promise { + ): Promise { const response = await this.client.runCommand( Convert.commandToJson({ secrets: { - update: { id, key, note, organizationId, value }, + update: { id, key, value, note, projectIds, organizationId }, }, }), ); - return Convert.toResponseForSecretResponse(response); + return handleResponse(Convert.toResponseForSecretResponse(response)); } - async delete(ids: string[]): Promise { + async delete(ids: string[]): Promise { const response = await this.client.runCommand( Convert.commandToJson({ secrets: { @@ -135,6 +148,86 @@ export class SecretsClient { }), ); - return Convert.toResponseForSecretsDeleteResponse(response); + return handleResponse(Convert.toResponseForSecretsDeleteResponse(response)); + } + + async sync(organizationId: string, lastSyncedDate?: Date): Promise { + const response = await this.client.runCommand( + Convert.commandToJson({ + secrets: { + sync: { organizationId, lastSyncedDate }, + }, + }), + ); + + return handleResponse(Convert.toResponseForSecretsSyncResponse(response)); + } +} + +export class ProjectsClient { + client: rust.BitwardenClient; + + constructor(client: rust.BitwardenClient) { + this.client = client; + } + + async get(id: string): Promise { + const response = await this.client.runCommand( + Convert.commandToJson({ + projects: { + get: { id }, + }, + }), + ); + + return handleResponse(Convert.toResponseForProjectResponse(response)); + } + + async create(name: string, organizationId: string): Promise { + const response = await this.client.runCommand( + Convert.commandToJson({ + projects: { + create: { name, organizationId }, + }, + }), + ); + + return handleResponse(Convert.toResponseForProjectResponse(response)); + } + + async list(organizationId: string): Promise { + const response = await this.client.runCommand( + Convert.commandToJson({ + projects: { + list: { organizationId }, + }, + }), + ); + + return handleResponse(Convert.toResponseForProjectsResponse(response)); + } + + async update(id: string, name: string, organizationId: string): Promise { + const response = await this.client.runCommand( + Convert.commandToJson({ + projects: { + update: { id, name, organizationId }, + }, + }), + ); + + return handleResponse(Convert.toResponseForProjectResponse(response)); + } + + async delete(ids: string[]): Promise { + const response = await this.client.runCommand( + Convert.commandToJson({ + projects: { + delete: { ids }, + }, + }), + ); + + return handleResponse(Convert.toResponseForProjectsDeleteResponse(response)); } }