Skip to content

Commit

Permalink
feat(cactus-plugin-ledger-connector-cdl-socketio): support subscripti…
Browse files Browse the repository at this point in the history
…on 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 <[email protected]>
  • Loading branch information
outSH authored and petermetz committed Oct 13, 2023
1 parent 704e201 commit 65c5cc9
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 57 deletions.
12 changes: 7 additions & 5 deletions packages/cactus-plugin-ledger-connector-cdl-socketio/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"`)
Expand Down Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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
Expand Down Expand Up @@ -87,7 +71,7 @@ export class ServerPlugin {

const responseData = await cdlRequest(
`trail_registration`,
getAccessTokenOrThrow(args),
args.method.authInfo,
{},
{
"cdl:EventId": typedArgs.eventId ?? "",
Expand Down Expand Up @@ -133,7 +117,7 @@ export class ServerPlugin {

const responseData = await cdlRequest(
`trail_acquisition/${sanitizeHtml(typedArgs.eventId)}`,
getAccessTokenOrThrow(args),
args.method.authInfo,
{
direction,
depth,
Expand Down Expand Up @@ -197,7 +181,7 @@ async function searchRequest(

const responseData = await cdlRequest(
searchType,
getAccessTokenOrThrow(args),
args.method.authInfo,
{},
{
searchType: typedArgs.searchType,
Expand All @@ -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];
}
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -43,7 +48,7 @@ const COMMON_HTTPS_AGENT = getHttpsAgent();

export async function cdlRequest(
url: string,
accessToken: [string, string],
authInfo: AuthInfoArgsType,
queryParams?: any,
dataPayload?: any,
) {
Expand All @@ -59,13 +64,12 @@ export async function cdlRequest(
httpsAgent: COMMON_HTTPS_AGENT,
method: httpMethod,
baseURL: configRead<string>("cdlApiGateway.url"),
url: `api/v1/${url}`,
url,
responseType: "json",
headers: {
"User-Agent": configRead<string>("userAgent", "CactiCDLConnector"),
Authorization: `Bearer ${accessToken[0]}`,
"Trust-Agent-Id": accessToken[1],
"Content-Type": "application/json;charset=UTF-8",
...getAuthorizationHeaders(authInfo),
},
params: queryParams,
data: dataPayload,
Expand All @@ -80,3 +84,35 @@ export async function cdlRequest(
throw error;
}
}

function getAuthorizationHeaders(
authInfo: AuthInfoArgsType,
): Record<string, string | number | boolean> {
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!",
);
}
}
Original file line number Diff line number Diff line change
@@ -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;
};
Original file line number Diff line number Diff line change
@@ -1,16 +1,29 @@
/**
* 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.
*/

//////////////////////////////////
// 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";
Expand Down Expand Up @@ -51,8 +64,7 @@ describe("CDL Connector manual tests", () => {
{},
{
type: "registerHistoryData",
accessToken: ACCESS_TOKEN,
trustAgentId: TOKEN_AGENT_ID,
authInfo,
},
args,
);
Expand All @@ -76,8 +88,7 @@ describe("CDL Connector manual tests", () => {
{},
{
type: "getLineage",
accessToken: ACCESS_TOKEN,
trustAgentId: TOKEN_AGENT_ID,
authInfo,
},
args,
);
Expand All @@ -97,8 +108,7 @@ describe("CDL Connector manual tests", () => {
{},
{
type: "searchByHeader",
accessToken: ACCESS_TOKEN,
trustAgentId: TOKEN_AGENT_ID,
authInfo,
},
args,
);
Expand All @@ -117,8 +127,7 @@ describe("CDL Connector manual tests", () => {
{},
{
type: "searchByGlobalData",
accessToken: ACCESS_TOKEN,
trustAgentId: TOKEN_AGENT_ID,
authInfo,
},
args,
);
Expand Down Expand Up @@ -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: "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
{
Expand Down

0 comments on commit 65c5cc9

Please sign in to comment.