Skip to content

Commit

Permalink
chore!(sdk): remove v1 support
Browse files Browse the repository at this point in the history
- Removes axios and XHR support - all requests now use fetch
- Removes `v1` path support for rewrap and public key
- Removes `entityObject`
- Removes AppIdAuthProvider
- Removes `upsert`
  • Loading branch information
dmihalcik-virtru committed Nov 25, 2024
1 parent ab40664 commit ae18fc2
Show file tree
Hide file tree
Showing 24 changed files with 186 additions and 836 deletions.
9 changes: 0 additions & 9 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -231,15 +231,6 @@ jobs:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: echo "- [Client Library](https://github.com/opentdf/web-sdk/pkgs/npm/client)">>$GITHUB_STEP_SUMMARY
- run: echo "- [Command Line Tool](https://github.com/opentdf/web-sdk/pkgs/npm/cli)">>$GITHUB_STEP_SUMMARY
- name: trigger xtest
run: >-
curl -XPOST -u "virtru-cloudnative:${{secrets.PERSONAL_ACCESS_TOKEN}}"
-H "Accept: application/vnd.github.everest-preview+json"
-H "Content-Type: application/json"
"https://api.github.com/repos/opentdf/backend/dispatches"
--data '{"event_type":"xtest","client_payload":{"version":"'${FULL_VERSION%%+*}'"}}'
env:
FULL_VERSION: ${{ steps.guess-build-metadata.outputs.FULL_VERSION }}
- name: Publish documentation to gh-pages
uses: JamesIves/[email protected]
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/roundtrip/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "web-sdk-roundtrip",
"version": "0.0.1",
"description": "Simple example to encrypt and decrypt files with quickstart backend.",
"description": "Simple example to encrypt and decrypt files.",
"scripts": {},
"dependencies": {
"@opentdf/ctl": "file:../../../cli/opentdf-ctl-0.1.0.tgz"
Expand Down
10 changes: 3 additions & 7 deletions .github/workflows/roundtrip/wait-and-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,9 @@ _configure_app() {
return 0
}

if [ $1 = backend ]; then
VITE_PROXY='{"/api":{"target":"http://localhost:5432","xfwd":true},"/auth":{"target":"http://localhost:5432","xfwd":true}}'
VITE_TDF_CFG='{"oidc":{"host":"http://localhost:65432/auth/realms/tdf","clientId":"browsertest"},"kas":"http://localhost:65432/api/kas","reader":"https://secure.virtru.com/start?htmlProtocol=1"}'
else # if [ $1 = platform ]; then
VITE_PROXY='{"/kas":{"target":"http://localhost:8080","xfwd":true},"/auth":{"target":"http://localhost:8888","xfwd":true}}'
VITE_TDF_CFG='{"oidc":{"host":"http://localhost:65432/auth/realms/opentdf","clientId":"browsertest"},"kas":"http://localhost:65432/kas","reader":"https://secure.virtru.com/start?htmlProtocol=1"}'
fi
VITE_PROXY='{"/kas":{"target":"http://localhost:8080","xfwd":true},"/auth":{"target":"http://localhost:8888","xfwd":true}}'
VITE_TDF_CFG='{"oidc":{"host":"http://localhost:65432/auth/realms/opentdf","clientId":"browsertest"},"kas":"http://localhost:65432/kas","reader":"https://secure.virtru.com/start?htmlProtocol=1"}'

export VITE_PROXY
export VITE_TDF_CFG

Expand Down
53 changes: 8 additions & 45 deletions lib/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions lib/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,6 @@
"watch": "(trap 'kill 0' SIGINT; npm run build && (npm run build:watch & npm run test -- --watch))"
},
"dependencies": {
"axios": "^1.6.1",
"axios-retry": "^3.9.0",
"base64-js": "^1.5.1",
"browser-fs-access": "^0.34.1",
"buffer-crc32": "^0.2.13",
Expand Down
116 changes: 63 additions & 53 deletions lib/src/access.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { type AuthProvider } from './auth/auth.js';
import {
ConfigurationError,
InvalidFileError,
NetworkError,
PermissionDeniedError,
Expand All @@ -8,14 +9,16 @@ import {
} from './errors.js';
import { pemToCryptoPublicKey, validateSecureUrl } from './utils.js';

export class RewrapRequest {
signedRequestToken = '';
}
export type RewrapRequest = {
signedRequestToken: string;
};

export class RewrapResponse {
entityWrappedKey = '';
sessionPublicKey = '';
}
export type RewrapResponse = {
metadata: Record<string, unknown>;
entityWrappedKey: string;
sessionPublicKey: string;
schemaVersion: string;
};

/**
* Get a rewrapped access key to the document, if possible
Expand All @@ -40,8 +43,10 @@ export async function fetchWrappedKey(
body: JSON.stringify(requestBody),
});

let response: Response;

try {
const response = await fetch(req.url, {
response = await fetch(req.url, {
method: req.method,
mode: 'cors', // no-cors, *cors, same-origin
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
Expand All @@ -51,28 +56,33 @@ export async function fetchWrappedKey(
referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
body: req.body as BodyInit,
});
} catch (e) {
throw new NetworkError(`unable to fetch wrapped key from [${url}]`, e);
}

if (!response.ok) {
switch (response.status) {
case 400:
throw new InvalidFileError(
`400 for [${req.url}]: rewrap failure [${await response.text()}]`
);
case 401:
throw new UnauthenticatedError(`401 for [${req.url}]`);
case 403:
throw new PermissionDeniedError(`403 for [${req.url}]`);
default:
throw new NetworkError(
`${req.method} ${req.url} => ${response.status} ${response.statusText}`
if (!response.ok) {
switch (response.status) {
case 400:
throw new InvalidFileError(
`400 for [${req.url}]: rewrap bad request [${await response.text()}]`
);
case 401:
throw new UnauthenticatedError(`401 for [${req.url}]; rewrap auth failure`);
case 403:
throw new PermissionDeniedError(`403 for [${req.url}]; rewrap permission denied`);
default:
if (response.status >= 500) {
throw new ServiceError(
`${response.status} for [${req.url}]: rewrap failure due to service error [${await response.text()}]`
);
}
}
throw new NetworkError(
`${req.method} ${req.url} => ${response.status} ${response.statusText}`
);
}

return response.json();
} catch (e) {
throw new NetworkError(`unable to fetch wrapped key from [${url}]: ${e}`);
}

return response.json();
}

export type KasPublicKeyAlgorithm = 'ec:secp256r1' | 'rsa:2048';
Expand Down Expand Up @@ -100,7 +110,7 @@ export type KasPublicKeyInfo = {
key: Promise<CryptoKey>;
};

async function noteInvalidPublicKey(url: string, r: Promise<CryptoKey>): Promise<CryptoKey> {
async function noteInvalidPublicKey(url: URL, r: Promise<CryptoKey>): Promise<CryptoKey> {
try {
return await r;
} catch (e) {
Expand All @@ -116,14 +126,36 @@ async function noteInvalidPublicKey(url: string, r: Promise<CryptoKey>): Promise
* the value from `${kas}/kas_public_key`.
*/
export async function fetchECKasPubKey(kasEndpoint: string): Promise<KasPublicKeyInfo> {
return fetchKasPubKey(kasEndpoint, 'ec:secp256r1');
}

export async function fetchKasPubKey(
kasEndpoint: string,
algorithm?: KasPublicKeyAlgorithm
): Promise<KasPublicKeyInfo> {
if (!kasEndpoint) {
throw new ConfigurationError('KAS definition not found');
}
// Logs insecure KAS. Secure is enforced in constructor
validateSecureUrl(kasEndpoint);
const pkUrlV2 = `${kasEndpoint}/v2/kas_public_key?algorithm=ec:secp256r1&v=2`;
const kasPubKeyResponseV2 = await fetch(pkUrlV2);
const infoStatic = { url: kasEndpoint, algorithm: algorithm || 'rsa:2048' };

const pkUrlV2 = new URL('/v2/kas_public_key?v=2', kasEndpoint);
if (!pkUrlV2) {
throw new ConfigurationError(`KAS definition invalid: [${kasEndpoint}]`);
}
pkUrlV2.searchParams.set('algorithm', infoStatic.algorithm);

let kasPubKeyResponseV2: Response;
try {
kasPubKeyResponseV2 = await fetch(pkUrlV2);
} catch (e) {
throw new NetworkError(`unable to fetch public key from [${pkUrlV2}]`, e);
}
if (!kasPubKeyResponseV2.ok) {
switch (kasPubKeyResponseV2.status) {
case 404:
// v2 not implemented, perhaps a legacy server
break;
throw new ConfigurationError(`404 for [${pkUrlV2}]`);
case 401:
throw new UnauthenticatedError(`401 for [${pkUrlV2}]`);
case 403:
Expand All @@ -133,28 +165,6 @@ export async function fetchECKasPubKey(kasEndpoint: string): Promise<KasPublicKe
`${pkUrlV2} => ${kasPubKeyResponseV2.status} ${kasPubKeyResponseV2.statusText}`
);
}
// most likely a server that does not implement v2 endpoint, so no key identifier
const pkUrlV1 = `${kasEndpoint}/kas_public_key?algorithm=ec:secp256r1`;
const r2 = await fetch(pkUrlV1);
if (!r2.ok) {
switch (r2.status) {
case 401:
throw new UnauthenticatedError(`401 for [${pkUrlV2}]`);
case 403:
throw new PermissionDeniedError(`403 for [${pkUrlV2}]`);
default:
throw new NetworkError(
`unable to load KAS public key from [${pkUrlV1}]. Received [${r2.status}:${r2.statusText}]`
);
}
}
const pem = await r2.json();
return {
key: noteInvalidPublicKey(pkUrlV1, pemToCryptoPublicKey(pem)),
publicKey: pem,
url: kasEndpoint,
algorithm: 'ec:secp256r1',
};
}
const jsonContent = await kasPubKeyResponseV2.json();
const { publicKey, kid }: KasPublicKeyInfo = jsonContent;
Expand Down
Loading

0 comments on commit ae18fc2

Please sign in to comment.