From 65c5cc999ee0ebf83c2255f868654f213002ff10 Mon Sep 17 00:00:00 2001 From: Michal Bajer Date: Thu, 12 Oct 2023 09:48:59 +0000 Subject: [PATCH] feat(cactus-plugin-ledger-connector-cdl-socketio): support subscription key auth - Add alternative auth method using subscriptionKey instead of accessToken. - Both auth methods are supported but can't be used at the same time. - Adjust manual test script to work with subscriptionKey. - Build manual script as part of the main build. - Update README. - Remove default `api/v1/` URL prefix for compatibility with https://en-portal.research.global.fujitsu.com/ Signed-off-by: Michal Bajer --- .../README.md | 12 +-- .../main/typescript/connector/ServerPlugin.ts | 39 +-------- .../main/typescript/connector/cdl-request.ts | 44 +++++++++- .../main/typescript/connector/type-defs.ts | 59 +++++++++++++ .../integration/cdl-connector-manual.test.ts | 87 ++++++++++++++++--- .../tsconfig.json | 3 +- 6 files changed, 187 insertions(+), 57 deletions(-) create mode 100644 packages/cactus-plugin-ledger-connector-cdl-socketio/src/main/typescript/connector/type-defs.ts diff --git a/packages/cactus-plugin-ledger-connector-cdl-socketio/README.md b/packages/cactus-plugin-ledger-connector-cdl-socketio/README.md index 0c6d63976b..b76e7575dc 100644 --- a/packages/cactus-plugin-ledger-connector-cdl-socketio/README.md +++ b/packages/cactus-plugin-ledger-connector-cdl-socketio/README.md @@ -36,7 +36,7 @@ This plugin provides `Cacti` a way to interact with Fujitsu CDL networks. Using #### Configuring CDL API Gateway Access -- Set the base URL of GW service in `cdlApiGateway.url`. Do not include `api/v1`, just the base URL. (example: `"http://localhost:3000"`). +- Set the base URL of GW service in `cdlApiGateway.url` (example: `"http://localhost:3000"`). - If the service certificate is signed with a known CA (node uses Mozilla DB), then you can skip the next steps. - If the service is signed with unknown CA, you can specify the gateway certificate to trust manually: - Set `cdlApiGateway.caPath` to path of API Gateway certificate (in PEM format). (example: `"/etc/cactus/connector-cdl-socketio/CA/cdl-api-gateway-ca.pem"`) @@ -67,14 +67,16 @@ npm run start ## Manual Tests -- There are no automatic tests for this plugin. -- `cdl-connector-manual.test` contains a Jest test script that will check every implemented operation on a running CDL instance. +- There are no automatic tests for this plugin because there's no private instance of CDL available at a time. +- `cdl-connector-manual.test` contains a Jest test script that will check every implemented operation on a running CDL service. - **You need access to a running instance of CDL in order to run this script.** - Before running the script you must update the following variables in it: - - `ACCESS_TOKEN` - JWT token for authorization in CDL gateway. - - `TOKEN_AGENT_ID` - Token agent ID. + - `authInfo` - either `accessToken` or `subscriptionKey` based configuration. - `VALIDATOR_KEY_PATH` - Path to validator public certificate. - Script can be used as a quick reference for using this connector plugin. +- Since script is not part of project jest suite, to run in execute the following commands from a package dir: + - `npm run build` + - `npx jest dist/lib/test/typescript/integration/cdl-connector-manual.test.js` ## Contributing diff --git a/packages/cactus-plugin-ledger-connector-cdl-socketio/src/main/typescript/connector/ServerPlugin.ts b/packages/cactus-plugin-ledger-connector-cdl-socketio/src/main/typescript/connector/ServerPlugin.ts index 2bd613c1a3..36f467138d 100644 --- a/packages/cactus-plugin-ledger-connector-cdl-socketio/src/main/typescript/connector/ServerPlugin.ts +++ b/packages/cactus-plugin-ledger-connector-cdl-socketio/src/main/typescript/connector/ServerPlugin.ts @@ -1,3 +1,4 @@ +import { FunctionArgsType } from "./type-defs"; import { read as configRead } from "../common/core/config"; import { cdlRequest } from "./cdl-request"; import sanitizeHtml from "sanitize-html"; @@ -8,23 +9,6 @@ import { getLogger } from "log4js"; const logger = getLogger("ServerPlugin[" + process.pid + "]"); logger.level = configRead("logLevel", "info"); -type SupportedFunctions = - | "registerHistoryData" - | "getLineage" - | "searchByHeader" - | "searchByGlobalData" - | "status"; - -type FunctionArgsType = { - method: { - type: SupportedFunctions; - accessToken?: string; - trustAgentId?: string; - }; - args: any; - reqID?: string; -}; - /* * ServerPlugin * Class definition for server plugins @@ -87,7 +71,7 @@ export class ServerPlugin { const responseData = await cdlRequest( `trail_registration`, - getAccessTokenOrThrow(args), + args.method.authInfo, {}, { "cdl:EventId": typedArgs.eventId ?? "", @@ -133,7 +117,7 @@ export class ServerPlugin { const responseData = await cdlRequest( `trail_acquisition/${sanitizeHtml(typedArgs.eventId)}`, - getAccessTokenOrThrow(args), + args.method.authInfo, { direction, depth, @@ -197,7 +181,7 @@ async function searchRequest( const responseData = await cdlRequest( searchType, - getAccessTokenOrThrow(args), + args.method.authInfo, {}, { searchType: typedArgs.searchType, @@ -211,18 +195,3 @@ async function searchRequest( return responseData; } - -function getAccessTokenOrThrow(args: FunctionArgsType): [string, string] { - const accessToken = args?.method?.accessToken; - const trustAgentId = args?.method?.trustAgentId; - - if (!accessToken) { - throw new Error("Missing CDL accessToken"); - } - - if (!trustAgentId) { - throw new Error("Missing CDL trustAgentId"); - } - - return [accessToken, trustAgentId]; -} diff --git a/packages/cactus-plugin-ledger-connector-cdl-socketio/src/main/typescript/connector/cdl-request.ts b/packages/cactus-plugin-ledger-connector-cdl-socketio/src/main/typescript/connector/cdl-request.ts index 59aa4bcadf..63d110139e 100644 --- a/packages/cactus-plugin-ledger-connector-cdl-socketio/src/main/typescript/connector/cdl-request.ts +++ b/packages/cactus-plugin-ledger-connector-cdl-socketio/src/main/typescript/connector/cdl-request.ts @@ -1,3 +1,8 @@ +import { + AuthInfoArgsType, + isAuthInfoAccessTokenArgsType, + isAuthInfoSubscriptionKeyArgsType, +} from "./type-defs"; import { read as configRead } from "../common/core/config"; import axios from "axios"; import https from "https"; @@ -43,7 +48,7 @@ const COMMON_HTTPS_AGENT = getHttpsAgent(); export async function cdlRequest( url: string, - accessToken: [string, string], + authInfo: AuthInfoArgsType, queryParams?: any, dataPayload?: any, ) { @@ -59,13 +64,12 @@ export async function cdlRequest( httpsAgent: COMMON_HTTPS_AGENT, method: httpMethod, baseURL: configRead("cdlApiGateway.url"), - url: `api/v1/${url}`, + url, responseType: "json", headers: { "User-Agent": configRead("userAgent", "CactiCDLConnector"), - Authorization: `Bearer ${accessToken[0]}`, - "Trust-Agent-Id": accessToken[1], "Content-Type": "application/json;charset=UTF-8", + ...getAuthorizationHeaders(authInfo), }, params: queryParams, data: dataPayload, @@ -80,3 +84,35 @@ export async function cdlRequest( throw error; } } + +function getAuthorizationHeaders( + authInfo: AuthInfoArgsType, +): Record { + if ( + isAuthInfoAccessTokenArgsType(authInfo) && + isAuthInfoSubscriptionKeyArgsType(authInfo) + ) { + throw new Error( + "Mixed authInfo configuration detected - use either accessToken or subscriptionKey!", + ); + } + + if (isAuthInfoAccessTokenArgsType(authInfo)) { + return { + Authorization: `Bearer ${authInfo.accessToken}`, + "Trust-Agent-Id": authInfo.trustAgentId, + }; + } else if (isAuthInfoSubscriptionKeyArgsType(authInfo)) { + return { + "Ocp-Apim-Subscription-Key": authInfo.subscriptionKey, + "Trust-User-Id": authInfo.trustUserId, + "Trust-User-Role": authInfo.trustUserRole, + "Trust-Agent-Id": authInfo.trustAgentId, + "Trust-Agent-Role": authInfo.trustAgentRole, + }; + } else { + throw new Error( + "Missing authInfo information or information not complete!", + ); + } +} diff --git a/packages/cactus-plugin-ledger-connector-cdl-socketio/src/main/typescript/connector/type-defs.ts b/packages/cactus-plugin-ledger-connector-cdl-socketio/src/main/typescript/connector/type-defs.ts new file mode 100644 index 0000000000..121bb81021 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-cdl-socketio/src/main/typescript/connector/type-defs.ts @@ -0,0 +1,59 @@ +import { type } from "os"; + +export type SupportedFunctions = + | "registerHistoryData" + | "getLineage" + | "searchByHeader" + | "searchByGlobalData" + | "status"; + +export type AuthInfoAccessTokenArgsType = { + accessToken: string; + trustAgentId: string; +}; + +export type AuthInfoSubscriptionKeyArgsType = { + subscriptionKey: string; + trustAgentId: string; + trustAgentRole: string; + trustUserId: string; + trustUserRole: string; +}; + +export type AuthInfoArgsType = + | AuthInfoAccessTokenArgsType + | AuthInfoSubscriptionKeyArgsType; + +export function isAuthInfoAccessTokenArgsType( + authInfo: AuthInfoArgsType, +): authInfo is AuthInfoAccessTokenArgsType { + const typedAuthInfo = authInfo as AuthInfoAccessTokenArgsType; + return ( + typedAuthInfo && + typeof typedAuthInfo.accessToken !== "undefined" && + typeof typedAuthInfo.trustAgentId !== "undefined" + ); +} + +export function isAuthInfoSubscriptionKeyArgsType( + authInfo: AuthInfoArgsType, +): authInfo is AuthInfoSubscriptionKeyArgsType { + const typedAuthInfo = authInfo as AuthInfoSubscriptionKeyArgsType; + return ( + typedAuthInfo && + typeof typedAuthInfo.subscriptionKey !== "undefined" && + typeof typedAuthInfo.trustAgentId !== "undefined" && + typeof typedAuthInfo.trustAgentRole !== "undefined" && + typeof typedAuthInfo.trustUserId !== "undefined" && + typeof typedAuthInfo.trustUserRole !== "undefined" + ); +} + +export type FunctionArgsType = { + method: { + type: SupportedFunctions; + authInfo: AuthInfoArgsType; + }; + args: any; + reqID?: string; +}; diff --git a/packages/cactus-plugin-ledger-connector-cdl-socketio/src/test/typescript/integration/cdl-connector-manual.test.ts b/packages/cactus-plugin-ledger-connector-cdl-socketio/src/test/typescript/integration/cdl-connector-manual.test.ts index 621de2b133..a4aa29f07c 100644 --- a/packages/cactus-plugin-ledger-connector-cdl-socketio/src/test/typescript/integration/cdl-connector-manual.test.ts +++ b/packages/cactus-plugin-ledger-connector-cdl-socketio/src/test/typescript/integration/cdl-connector-manual.test.ts @@ -1,6 +1,6 @@ /** * Manual tests for CDL connector. - * Must be exectued on Azure environment with access to CDL service. + * Must be exectued with access to CDL service. * Check out CDL connector readme for instructions on how to run these tests. */ @@ -8,9 +8,22 @@ // Constants ////////////////////////////////// -const ACCESS_TOKEN = "_____FILL_TOKEN_HERE_____" -const TOKEN_AGENT_ID = "_____FILL_AGENT_ID_HERE_____" -const VALIDATOR_KEY_PATH = "_____FILL_KEY_PATH_HERE_____" +// Setup: Start validator first and store it's crt under this path +const VALIDATOR_KEY_PATH = + "/etc/cactus/connector-cdl-socketio/CA/connector.crt"; + +// Setup: Obtain eitehr accessToken or subscription key and fill matching authInfo structure below. +// const authInfo = { +// accessToken: "_____accessToken_____" +// trustAgentId: "_____trustAgentId_____", +// }; +const authInfo = { + subscriptionKey: "_____subscriptionKey_____", + trustAgentId: "_____trustAgentId_____", + trustAgentRole: "_____trustAgentRole_____", + trustUserId: "_____trustUserId_____", + trustUserRole: "_____trustUserRole_____", +}; const testLogLevel: LogLevelDesc = "info"; const sutLogLevel: LogLevelDesc = "info"; @@ -51,8 +64,7 @@ describe("CDL Connector manual tests", () => { {}, { type: "registerHistoryData", - accessToken: ACCESS_TOKEN, - trustAgentId: TOKEN_AGENT_ID, + authInfo, }, args, ); @@ -76,8 +88,7 @@ describe("CDL Connector manual tests", () => { {}, { type: "getLineage", - accessToken: ACCESS_TOKEN, - trustAgentId: TOKEN_AGENT_ID, + authInfo, }, args, ); @@ -97,8 +108,7 @@ describe("CDL Connector manual tests", () => { {}, { type: "searchByHeader", - accessToken: ACCESS_TOKEN, - trustAgentId: TOKEN_AGENT_ID, + authInfo, }, args, ); @@ -117,8 +127,7 @@ describe("CDL Connector manual tests", () => { {}, { type: "searchByGlobalData", - accessToken: ACCESS_TOKEN, - trustAgentId: TOKEN_AGENT_ID, + authInfo, }, args, ); @@ -212,6 +221,60 @@ describe("CDL Connector manual tests", () => { expect(response.data.status).toEqual("OK."); }); + test( + "Request fails when authInfo is missing", + async () => { + const response = await apiClient.sendSyncRequest( + {}, + { + type: "registerHistoryData", + }, + { + eventId: "", + lineageId: "", + tags: {}, + properties: { + prop1: "shouldFail", + prop2: "shouldFail", + }, + }, + ); + expect(response.status).toEqual(504); + }, + syncReqTimeout * 2, + ); + + test( + "Request fails when mixed authInfo is used", + async () => { + const response = await apiClient.sendSyncRequest( + {}, + { + type: "registerHistoryData", + authInfo: { + accessToken: "foo-accessToken", + subscriptionKey: "foo-subscriptionKey", + trustAgentId: "foo-trustAgentId", + trustAgentRole: "foo-trustAgentRole", + trustUserId: "foo-trustUserId", + trustUserRole: "foo-trustUserRole", + }, + }, + { + eventId: "", + lineageId: "", + tags: {}, + properties: { + prop1: "shouldFail", + prop2: "shouldFail", + }, + }, + ); + expect(response.status).toEqual(504); + }, + syncReqTimeout * 2, + ); + test("Register single history data", async () => { const newEvent = await registerHistoryDataOnCDL({ eventId: "", diff --git a/packages/cactus-plugin-ledger-connector-cdl-socketio/tsconfig.json b/packages/cactus-plugin-ledger-connector-cdl-socketio/tsconfig.json index ef6d560e46..ac1f3bd372 100644 --- a/packages/cactus-plugin-ledger-connector-cdl-socketio/tsconfig.json +++ b/packages/cactus-plugin-ledger-connector-cdl-socketio/tsconfig.json @@ -15,7 +15,8 @@ "./src/main/typescript/common/core/bin/*.ts", "./src/main/typescript/common/core/config/*.ts", "./src/main/typescript/connector/*.ts", - "./src/main/typescript/*.ts" + "./src/main/typescript/*.ts", + "./src/test/typescript/integration/*.ts" ], "references": [ {