diff --git a/.github/workflows/roundtrip/wait-and-test.sh b/.github/workflows/roundtrip/wait-and-test.sh index 8f14e589..8ca774ca 100755 --- a/.github/workflows/roundtrip/wait-and-test.sh +++ b/.github/workflows/roundtrip/wait-and-test.sh @@ -17,7 +17,7 @@ _configure_app() { echo "[ERROR] Couldn't ci roundtrip command line app" return 1 fi - if ! npm i "../../../cli/opentdf-cli-${app_version}.tgz"; then + if ! npm i "../../../cli/opentdf-cli-${app_version}.tgz"; then return 1 fi return 0 @@ -42,50 +42,48 @@ _wait-for() { exit 1 } -_init_server() -{ - output=$(mktemp) - if ! cd "${WEB_APP_DIR}"; then - echo "[ERROR] unable to cd ${WEB_APP_DIR}" - exit 2 +_init_server() { + output=$(mktemp) + if ! cd "${WEB_APP_DIR}"; then + echo "[ERROR] unable to cd ${WEB_APP_DIR}" + exit 2 + fi + npm uninstall @opentdf/client + if ! npm ci; then + echo "[ERROR] Couldn't ci web-app" + exit 2 + fi + if ! npm i "../lib/opentdf-client-${app_version}.tgz"; then + ls -ls ../lib/ + echo "[ERROR] Couldn't install @opentdf/client tarball" + return 1 + fi + npm run dev &>"$output" & + server_pid=$! + echo "Server pid: $server_pid" + echo "Output: $output" + echo "Wait:" + limit=5 + for i in $(seq 1 $limit); do + if grep -q -i 'ready' "$output"; then + return 0 fi - npm uninstall @opentdf/client - if ! npm ci; then - echo "[ERROR] Couldn't ci web-app" - exit 2 + if ! ps $server_pid >/dev/null; then + echo "The server died" >&2 + cat "${output}" + exit 1 fi - if ! npm i "../lib/opentdf-client-${app_version}.tgz"; then - ls -ls ../lib/ - echo "[ERROR] Couldn't install @opentdf/client tarball" - return 1 + if [[ $i == "$limit" ]]; then + echo "[WARN] Breaking _init_server loop after ${limit} iterations" + cat "${output}" + break fi - npm run dev &> "$output" & - server_pid=$! - echo "Server pid: $server_pid" - echo "Output: $output" - echo "Wait:" - limit=5 - for i in $(seq 1 $limit); do - if grep -q -i 'ready' "$output"; then - return 0 - fi - if ! ps $server_pid > /dev/null; then - echo "The server died" >&2 - cat "${output}" - exit 1 - fi - if [[ $i == "$limit" ]]; then - echo "[WARN] Breaking _init_server loop after ${limit} iterations" - cat "${output}" - break - fi - sleep_for=$((5 + i * i * 2)) - echo "[INFO] retrying in ${sleep_for} seconds... ( ${i} / $limit ) ..." - sleep ${sleep_for} - done + sleep_for=$((5 + i * i * 2)) + echo "[INFO] retrying in ${sleep_for} seconds... ( ${i} / $limit ) ..." + sleep ${sleep_for} + done } - if ! _configure_app; then echo "[ERROR] Couldn't configure our library and app" exit 2 @@ -105,7 +103,7 @@ if ! cd "${WEB_APP_DIR}"; then exit 2 fi -if ! cd tests; then +if ! cd tests; then echo "[ERROR] Couldn't open web integration tests folder" exit 2 fi diff --git a/cli/package-lock.json b/cli/package-lock.json index 4627e5b7..944545f9 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1421,7 +1421,7 @@ "node_modules/@opentdf/client": { "version": "0.4.0", "resolved": "file:../lib/opentdf-client-0.4.0.tgz", - "integrity": "sha512-45eUeVcp77krXJsXh7bTSHo9JFnnhH24Fvrs2M9bvXVgMnMjd8hyD1DQGVg83X0ACqAU+hQRp/xzR9Fm2WmgNQ==", + "integrity": "sha512-l7Cpz3miOWsJzA7YF4v1MFJKlorLlMomFLlHwTfCrKzs07YvDtaHJ49oxjIB47C8WA1oZyq4Lpv8CNmtpHa2dQ==", "license": "BSD-3-Clause-Clear", "dependencies": { "@aws-sdk/client-s3": "3.197.0", @@ -5696,7 +5696,7 @@ }, "@opentdf/client": { "version": "file:../lib/opentdf-client-0.4.0.tgz", - "integrity": "sha512-45eUeVcp77krXJsXh7bTSHo9JFnnhH24Fvrs2M9bvXVgMnMjd8hyD1DQGVg83X0ACqAU+hQRp/xzR9Fm2WmgNQ==", + "integrity": "sha512-l7Cpz3miOWsJzA7YF4v1MFJKlorLlMomFLlHwTfCrKzs07YvDtaHJ49oxjIB47C8WA1oZyq4Lpv8CNmtpHa2dQ==", "requires": { "@aws-sdk/client-s3": "3.197.0", "axios": "^1.2.3", diff --git a/lib/tdf3/src/client/DecoratedReadableStream.ts b/lib/tdf3/src/client/DecoratedReadableStream.ts index 3d6a1ab3..3d6917c8 100644 --- a/lib/tdf3/src/client/DecoratedReadableStream.ts +++ b/lib/tdf3/src/client/DecoratedReadableStream.ts @@ -20,6 +20,7 @@ export async function streamToBuffer(stream: ReadableStream): Promis export abstract class DecoratedReadableStream { KEK: null | string; algorithm: string; + policyUuid?: string; tdfSize: number; stream: ReadableStream; on: NodeJS.EventEmitter['on']; diff --git a/lib/tdf3/src/client/builders.ts b/lib/tdf3/src/client/builders.ts index b9ea6a0e..70a0c3ae 100644 --- a/lib/tdf3/src/client/builders.ts +++ b/lib/tdf3/src/client/builders.ts @@ -4,13 +4,13 @@ import axios from 'axios'; import { arrayBufferToBuffer, inBrowser } from '../utils/index.js'; import { AttributeValidator } from './validation/index.js'; import { AttributeObject, Policy } from '../models/index.js'; -import { RcaParams } from '../tdf.js'; +import { type RcaParams, type RcaLink } from '../tdf.js'; import { Binary } from '../binary.js'; import { IllegalArgumentError, IllegalEnvError } from '../errors.js'; import { PemKeyPair } from '../crypto/declarations.js'; import PolicyObject from '../../../src/tdf/PolicyObject.js'; -import { type EntityObject } from '../../../src/tdf/EntityObject.js'; +import { EntityObject } from '../../../src/tdf/EntityObject.js'; const { get } = axios; @@ -203,6 +203,12 @@ class EncryptParamsBuilder { * @return {EncryptParamsBuilder} - this object. */ withStreamSource(readStream: ReadableStream): EncryptParamsBuilder { + if (!readStream?.getReader) { + throw new Error( + `Source must be a WebReadableStream. Run node streams through stream.Readable.toWeb()` + ); + } + this.setStreamSource(readStream); return this; } @@ -700,6 +706,12 @@ class DecryptParamsBuilder { * @return {DecryptParamsBuilder} - this object. */ withStreamSource(stream: ReadableStream) { + if (!stream?.getReader) { + throw new Error( + `Source must be a WebReadableStream. Run node streams through stream.Readable.toWeb()` + ); + } + this.setStreamSource(stream); return this; } @@ -795,8 +807,24 @@ class DecryptParamsBuilder { /** * @param rcaParams */ - setRcaSource(rcaParams: RcaParams) { - this._params.rcaSource = rcaParams; + setRcaSource(rcaParams: RcaParams | RcaLink) { + let params; + + if (typeof rcaParams === 'object') { + params = { ...rcaParams }; + } else if (typeof rcaParams === 'string') { + params = Object.fromEntries(new URLSearchParams(rcaParams)); + } + + if (!params?.pu || !params?.wu || !params?.wk || !params?.al) { + throw new Error(`RCA link [${rcaParams}] is missing parameters!`); + } + + const { pu, wu, wk, al } = params; + + this.setUrlSource(wu); + + this._params.rcaSource = { pu, wu, wk, al }; } /** @@ -804,7 +832,7 @@ class DecryptParamsBuilder { * @param rcaParams * @returns {DecryptParamsBuilder} */ - withRcaSource(rcaParams: RcaParams): DecryptParamsBuilder { + withRcaSource(rcaParams: RcaParams | RcaLink): DecryptParamsBuilder { this.setRcaSource(rcaParams); return this; } diff --git a/lib/tdf3/src/client/index.ts b/lib/tdf3/src/client/index.ts index 7c074859..104ffa07 100644 --- a/lib/tdf3/src/client/index.ts +++ b/lib/tdf3/src/client/index.ts @@ -100,6 +100,7 @@ export interface ClientConfig { clientId?: string; dpopEnabled?: boolean; kasEndpoint?: string; + easEndpoint?: string; // DEPRECATED Ignored keyRewrapEndpoint?: string; // DEPRECATED Ignored @@ -111,7 +112,6 @@ export interface ClientConfig { externalJwt?: string; authProvider?: AuthProvider | AppIdAuthProvider; readerUrl?: string; - easEndpoint?: string; entityObjectEndpoint?: string; } @@ -123,6 +123,8 @@ export class Client { kasPublicKey?: string; + easEndpoint?: string; + clientId?: string; authProvider?: AuthProvider | AppIdAuthProvider; @@ -261,6 +263,8 @@ export class Client { payloadKey, }: EncryptParams): Promise { if (rcaSource && asHtml) throw new Error('rca links should be used only with zip format'); + if (rcaSource && !this.kasEndpoint) + throw new Error('rca links require a kasEndpoint url to be set'); const keypair: PemKeyPair = await this._getOrCreateKeypair(opts); const policyObject = await this._createPolicyObject(scope); @@ -294,13 +298,8 @@ export class Client { const byteLimit = asHtml ? HTML_BYTE_LIMIT : GLOBAL_BYTE_LIMIT; const stream = await tdf.writeStream(byteLimit, rcaSource, payloadKey); // Looks like invalid calls | stream.upsertResponse equals empty array? - if ( - rcaSource && - stream.upsertResponse && - stream.upsertResponse[0][0]?.storageLinks?.payload?.upload - ) { - const url = stream.upsertResponse[0][0].storageLinks.payload.upload; - await uploadBinaryToS3(stream.stream, url, stream.tdfSize); + if (rcaSource) { + stream.policyUuid = policyObject.uuid; } if (!asHtml) { return stream; diff --git a/lib/tdf3/src/tdf.ts b/lib/tdf3/src/tdf.ts index 187b3e63..7478791e 100644 --- a/lib/tdf3/src/tdf.ts +++ b/lib/tdf3/src/tdf.ts @@ -70,6 +70,8 @@ export type RcaParams = { al: string; }; +export type RcaLink = string; + type Metadata = { connectOptions: { testUrl: string; diff --git a/web-app/package-lock.json b/web-app/package-lock.json index 30db449d..2a76f8ea 100644 --- a/web-app/package-lock.json +++ b/web-app/package-lock.json @@ -2163,7 +2163,7 @@ "node_modules/@opentdf/client": { "version": "0.4.0", "resolved": "file:../lib/opentdf-client-0.4.0.tgz", - "integrity": "sha512-45eUeVcp77krXJsXh7bTSHo9JFnnhH24Fvrs2M9bvXVgMnMjd8hyD1DQGVg83X0ACqAU+hQRp/xzR9Fm2WmgNQ==", + "integrity": "sha512-l7Cpz3miOWsJzA7YF4v1MFJKlorLlMomFLlHwTfCrKzs07YvDtaHJ49oxjIB47C8WA1oZyq4Lpv8CNmtpHa2dQ==", "license": "BSD-3-Clause-Clear", "dependencies": { "@aws-sdk/client-s3": "3.197.0", @@ -7073,7 +7073,7 @@ }, "@opentdf/client": { "version": "file:../lib/opentdf-client-0.4.0.tgz", - "integrity": "sha512-45eUeVcp77krXJsXh7bTSHo9JFnnhH24Fvrs2M9bvXVgMnMjd8hyD1DQGVg83X0ACqAU+hQRp/xzR9Fm2WmgNQ==", + "integrity": "sha512-l7Cpz3miOWsJzA7YF4v1MFJKlorLlMomFLlHwTfCrKzs07YvDtaHJ49oxjIB47C8WA1oZyq4Lpv8CNmtpHa2dQ==", "requires": { "@aws-sdk/client-s3": "3.197.0", "axios": "^1.2.3",