Skip to content

Commit

Permalink
test(cmd-api-server): jestify jwt-socketio-endpoint-authorization.test
Browse files Browse the repository at this point in the history
Co-authored-by: Peter Somogyvari <peter.somogyvari@accenture.com>

Signed-off-by: Peter Somogyvari <peter.somogyvari@accenture.com>
  • Loading branch information
Leeyoungone authored and petermetz committed Aug 18, 2023
1 parent 79df7af commit b218bf7
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 66 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ jobs:
JEST_TEST_PATTERN: packages/cactus-cmd-api-server/src/test/typescript/(unit|integration|benchmark)/.*/*.test.ts
JEST_TEST_RUNNER_DISABLED: false
TAPE_TEST_PATTERN: >-
--files={./packages/cactus-cmd-api-server/src/test/typescript/benchmark/artillery-api-benchmark.test.ts,./packages/cactus-cmd-api-server/src/test/typescript/integration/jwt-socketio-endpoint-authorization.test.ts,./packages/cactus-cmd-api-server/src/test/typescript/integration/plugin-import-from-github.test.ts,./packages/cactus-cmd-api-server/src/test/typescript/integration/plugin-import-without-install.test.ts,./packages/cactus-cmd-api-server/src/test/typescript/integration/remote-plugin-imports.test.ts,./packages/cactus-cmd-api-server/src/test/typescript/unit/config/self-signed-certificate-generator/certificates-work-for-mutual-tls.test.ts,./packages/cactus-cmd-api-server/src/test/typescript/unit/config/self-signed-certificate-generator/generates-working-certificates.test.ts,./packages/cactus-cmd-api-server/src/test/typescript/unit/grpc-js-proto-loader-client-healthcheck.test.ts,./packages/cactus-cmd-api-server/src/test/typescript/unit/grpc-proto-gen-ts-client-healthcheck.test.ts,./packages/cactus-cmd-api-server/src/test/typescript/unit/grpc-proto-gen-ts-client-m-tls-enabled.test.ts,./packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-consortium-manual.test.ts,./packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-keychain-memory.test.ts,./packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-ledger-connector-fabric-0-7-0.test.ts,./packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-ledger-connector-quorum-0-7-0.test.ts}
--files={./packages/cactus-cmd-api-server/src/test/typescript/benchmark/artillery-api-benchmark.test.ts,./packages/cactus-cmd-api-server/src/test/typescript/integration/plugin-import-from-github.test.ts,./packages/cactus-cmd-api-server/src/test/typescript/integration/plugin-import-without-install.test.ts,./packages/cactus-cmd-api-server/src/test/typescript/integration/remote-plugin-imports.test.ts,./packages/cactus-cmd-api-server/src/test/typescript/unit/config/self-signed-certificate-generator/certificates-work-for-mutual-tls.test.ts,./packages/cactus-cmd-api-server/src/test/typescript/unit/config/self-signed-certificate-generator/generates-working-certificates.test.ts,./packages/cactus-cmd-api-server/src/test/typescript/unit/grpc-js-proto-loader-client-healthcheck.test.ts,./packages/cactus-cmd-api-server/src/test/typescript/unit/grpc-proto-gen-ts-client-healthcheck.test.ts,./packages/cactus-cmd-api-server/src/test/typescript/unit/grpc-proto-gen-ts-client-m-tls-enabled.test.ts,./packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-consortium-manual.test.ts,./packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-keychain-memory.test.ts,./packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-ledger-connector-fabric-0-7-0.test.ts,./packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-ledger-connector-quorum-0-7-0.test.ts}
TAPE_TEST_RUNNER_DISABLED: false
needs: build-dev
runs-on: ubuntu-20.04
Expand Down
1 change: 0 additions & 1 deletion .taprc
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ files:
- ./packages/cactus-test-plugin-htlc-eth-besu/src/test/typescript/integration/plugin-htlc-eth-besu/get-single-status-endpoint.test.ts
- ./packages/cactus-test-plugin-htlc-eth-besu/src/test/typescript/integration/plugin-htlc-eth-besu/openapi/openapi-validation.test.ts
- ./packages/cactus-cmd-api-server/src/test/typescript/benchmark/artillery-api-benchmark.test.ts
- ./packages/cactus-cmd-api-server/src/test/typescript/integration/jwt-socketio-endpoint-authorization.test.ts
- ./packages/cactus-cmd-api-server/src/test/typescript/integration/remote-plugin-imports.test.ts
- ./packages/cactus-cmd-api-server/src/test/typescript/integration/plugin-import-from-github.test.ts
- ./packages/cactus-cmd-api-server/src/test/typescript/integration/plugin-import-without-install.test.ts
Expand Down
1 change: 0 additions & 1 deletion jest.config.js

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

Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ export class ApiServerApiClient extends DefaultApi {
subject.error(ex);
});

socket.on("disconnect", (reason: string) => {
const { active } = socket;
log.error("[SocketIOClient] DISCONNECT=%s Active=%b", reason, active);
});

socket.on("connect_error", async (err) => {
log.debug("[SocketIOClient] CONNECT_ERROR: %o", err);
if (tokenProvider.isPresent()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import test, { Test } from "tape-promise/tape";
import "jest-extended";
import { v4 as uuidv4 } from "uuid";
import { generateKeyPair, exportSPKI, SignJWT } from "jose";
import type { Params as ExpressJwtOptions } from "express-jwt";
import type { AuthorizeOptions as SocketIoJwtOptions } from "@thream/socketio-jwt";

import { Constants } from "@hyperledger/cactus-core-api";
import { LoggerProvider, LogLevelDesc } from "@hyperledger/cactus-common";
import { IJoseFittingJwtParams } from "@hyperledger/cactus-common";

import {
Expand All @@ -16,18 +15,25 @@ import {
} from "../../../main/typescript/public-api";
import { ApiServerApiClient } from "../../../main/typescript/public-api";
import { ApiServerApiClientConfiguration } from "../../../main/typescript/public-api";
import { LogLevelDesc } from "@hyperledger/cactus-common";
import { AuthorizationProtocol } from "../../../main/typescript/config/authorization-protocol";
import { IAuthorizationConfig } from "../../../main/typescript/authzn/i-authorization-config";
import { lastValueFrom } from "rxjs";

const testCase = "API server enforces authorization for SocketIO endpoints";
const logLevel: LogLevelDesc = "TRACE";
const log = LoggerProvider.getOrCreate({
level: logLevel,
label: __filename,
});
describe("cmd-api-server:ApiServer", () => {
let apiServer: ApiServer;
let apiClientFixable: ApiServerApiClient;
let apiHost: string;
let validBearerToken: string;

const logLevel: LogLevelDesc = "WARN";

test(testCase, async (t: Test) => {
try {
afterAll(async () => {
apiServer.shutdown();
});

beforeAll(async () => {
const jwtKeyPair = await generateKeyPair("RS256", { modulusLength: 4096 });
const jwtPublicKey = await exportSPKI(jwtKeyPair.publicKey);
const expressJwtOptions: ExpressJwtOptions & IJoseFittingJwtParams = {
Expand All @@ -40,7 +46,7 @@ test(testCase, async (t: Test) => {
secret: jwtPublicKey,
algorithms: ["RS256"],
};
t.ok(expressJwtOptions, "Express JWT config truthy OK");
expect(expressJwtOptions).toBeTruthy();

const authorizationConfig: IAuthorizationConfig = {
unprotectedEndpointExemptions: [],
Expand All @@ -58,59 +64,62 @@ test(testCase, async (t: Test) => {
apiSrvOpts.apiPort = 0;
apiSrvOpts.cockpitPort = 0;
apiSrvOpts.grpcPort = 0;
apiSrvOpts.logLevel = logLevel;
apiSrvOpts.apiTlsEnabled = false;
apiSrvOpts.plugins = [];
const config = await configService.newExampleConfigConvict(apiSrvOpts);

const apiServer = new ApiServer({
apiServer = new ApiServer({
config: config.getProperties(),
});
test.onFinish(async () => await apiServer.shutdown());

const startResponse = apiServer.start();
await t.doesNotReject(startResponse, "API server started OK");
t.ok(startResponse, "API server start response truthy OK");
const startResponsePromise = apiServer.start();
await expect(startResponsePromise).toResolve();
const startResponse = await startResponsePromise;
expect(startResponse).toBeTruthy();

const addressInfoApi = (await startResponse).addressInfoApi;
const addressInfoApi = (await startResponsePromise).addressInfoApi;
const protocol = apiSrvOpts.apiTlsEnabled ? "https" : "http";
const { address, port } = addressInfoApi;
const apiHost = `${protocol}://${address}:${port}`;
apiHost = `${protocol}://${address}:${port}`;

const jwtPayload = { name: "Peter", location: "Albertirsa" };
const validJwt = await new SignJWT(jwtPayload)
.setProtectedHeader({ alg: "RS256" })
.setIssuer(expressJwtOptions.issuer)
.setAudience(expressJwtOptions.audience)
.sign(jwtKeyPair.privateKey);
t.ok(validJwt, "JWT signed truthy OK");
expect(validJwt).toBeTruthy();

const validBearerToken = `Bearer ${validJwt}`;
t.ok(validBearerToken, "validBearerToken truthy OK");
validBearerToken = `Bearer ${validJwt}`;
expect(validBearerToken).toBeTruthy();

const apiClientBad = new ApiServerApiClient(
apiClientFixable = new ApiServerApiClient(
new ApiServerApiClientConfiguration({
basePath: apiHost,
baseOptions: { headers: { Authorization: "Mr. Invalid Token" } },
logLevel: "TRACE",
logLevel,
tokenProvider: {
get: () => Promise.resolve(validBearerToken),
},
}),
);
});

const apiClientFixable = new ApiServerApiClient(
test(testCase, async () => {
const apiClientBad = new ApiServerApiClient(
new ApiServerApiClientConfiguration({
basePath: apiHost,
baseOptions: { headers: { Authorization: "Mr. Invalid Token" } },
logLevel: "TRACE",
tokenProvider: {
get: () => Promise.resolve(validBearerToken),
},
logLevel,
}),
);

const apiClientGood = new ApiServerApiClient(
new ApiServerApiClientConfiguration({
basePath: apiHost,
baseOptions: { headers: { Authorization: validBearerToken } },
logLevel: "TRACE",
logLevel,
tokenProvider: {
get: () => Promise.resolve(validBearerToken),
},
Expand All @@ -134,57 +143,53 @@ test(testCase, async (t: Test) => {
});
});

await t.rejects(
watchHealthcheckV1WithBadToken,
/Format is Authorization: Bearer \[token\]/,
"SocketIO connection rejected when JWT is invalid OK",
await expect(watchHealthcheckV1WithBadToken).rejects.toHaveProperty(
"message",
"Format is Authorization: Bearer [token]",
);
}

{
const resHc = await apiClientGood.getHealthCheckV1();
t.ok(resHc, "healthcheck response truthy OK");
t.equal(resHc.status, 200, "healthcheck response status === 200 OK");
t.equal(typeof resHc.data, "object", "typeof resHc.data is 'object' OK");
t.ok(resHc.data.createdAt, "resHc.data.createdAt truthy OK");
t.ok(resHc.data.memoryUsage, "resHc.data.memoryUsage truthy OK");
t.ok(resHc.data.memoryUsage.rss, "resHc.data.memoryUsage.rss truthy OK");
t.ok(resHc.data.success, "resHc.data.success truthy OK");
t.true(isHealthcheckResponse(resHc.data), "isHealthcheckResponse OK");
expect(resHc).toBeTruthy();
expect(resHc.status).toEqual(200);
expect(typeof resHc.data).toBe("object");
expect(resHc.data.createdAt).toBeTruthy();
expect(resHc.data.memoryUsage).toBeTruthy();
expect(resHc.data.memoryUsage.rss).toBeTruthy();
expect(resHc.data.success).toBeTruthy();
expect(isHealthcheckResponse(resHc.data)).toBeTruthy();
}

{
let idx = 0;
const healthchecks = await apiClientFixable.watchHealthcheckV1();
const sub = healthchecks.subscribe((next: HealthCheckResponse) => {
idx++;
t.ok(next, idx + " next healthcheck truthy OK");
t.equal(typeof next, "object", idx + "typeof next is 'object' OK");
t.ok(next.createdAt, idx + " next.createdAt truthy OK");
t.ok(next.memoryUsage, idx + " next.memoryUsage truthy OK");
t.ok(next.memoryUsage.rss, idx + " next.memoryUsage.rss truthy OK");
t.ok(next.success, idx + " next.success truthy OK");
t.true(isHealthcheckResponse(next), idx + " isHealthcheckResponse OK");
if (idx > 2) {
sub.unsubscribe();
}
expect(next).toBeTruthy();
expect(typeof next).toBe("object");
expect(next.createdAt).toBeTruthy();
expect(next.memoryUsage).toBeTruthy();
expect(next.memoryUsage.rss).toBeTruthy();
expect(next.success).toBeTruthy();
expect(isHealthcheckResponse(next)).toBeTrue();
});

const all = await healthchecks.toPromise();
t.comment("all=" + JSON.stringify(all));
const hcr = await lastValueFrom(healthchecks);
expect(isHealthcheckResponse(hcr)).toBeTruthy();

const resHc = await apiClientFixable.getHealthCheckV1();
t.ok(resHc, "healthcheck response truthy OK");
t.equal(resHc.status, 200, "healthcheck response status === 200 OK");
t.equal(typeof resHc.data, "object", "typeof resHc.data is 'object' OK");
t.ok(resHc.data.createdAt, "resHc.data.createdAt truthy OK");
t.ok(resHc.data.memoryUsage, "resHc.data.memoryUsage truthy OK");
t.ok(resHc.data.memoryUsage.rss, "resHc.data.memoryUsage.rss truthy OK");
t.ok(resHc.data.success, "resHc.data.success truthy OK");
t.true(isHealthcheckResponse(resHc.data), "isHealthcheckResponse OK");
expect(resHc).toBeTruthy();
expect(resHc.status).toEqual(200);
expect(typeof resHc.data).toBe("object");
expect(resHc.data.createdAt).toBeTruthy();
expect(resHc.data.memoryUsage).toBeTruthy();
expect(resHc.data.memoryUsage.rss).toBeTruthy();
expect(resHc.data.success).toBeTruthy();
expect(isHealthcheckResponse(resHc.data)).toBeTruthy();
}
t.end();
} catch (ex) {
log.error(ex);
t.fail("Exception thrown during test execution, see above for details!");
throw ex;
}
});
});

0 comments on commit b218bf7

Please sign in to comment.