Skip to content

Commit

Permalink
Validate Backbone Compatibility (#208)
Browse files Browse the repository at this point in the history
* feat: validate backbone version

* chore: upgrade backbone

* chore: capitalization

* refactor: function name, version number

* fix: no override

* refactor: add VersionController

* refactor: use anonymousService

* test: adapt tests

* refactor: naming

* refactor: remove versionClient

* refactor: rename to BackboneCompatibility

* refactor: separate test file

* fix: import

* fix: import

* refactor: imports / codestyle

* fix: make tests valid instead of hacky

* chore: naming

* refactor: move to own module

* fix: codestyle

* chore: undo methods only use for accessing in tests

* chore: audit fix

* fix: naming

* chore: make easier to understand

* refactor: return more information on why the version is (not) compatible

* fix: tests

* fix: tests

* chore: remove unused error

* chore: codestyle

* refactor: shorthand

* chore: naming

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: Julian König <[email protected]>
Co-authored-by: Julian König <[email protected]>
  • Loading branch information
4 people authored Aug 26, 2024
1 parent c5b3499 commit 119fd8b
Show file tree
Hide file tree
Showing 16 changed files with 242 additions and 6 deletions.
2 changes: 1 addition & 1 deletion .ci/runChecks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ npm run build:node
npm run lint:eslint
npm run lint:prettier
npx -ws license-check
npx better-npm-audit audit --exclude 1096302,1098615
npx better-npm-audit audit --exclude 1096302
6 changes: 3 additions & 3 deletions package-lock.json

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

5 changes: 5 additions & 0 deletions packages/runtime/src/Runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import {
AccountController,
AnonymousTokenController,
BackboneCompatibilityController,
ChallengeController,
DeviceController,
DevicesController,
Expand Down Expand Up @@ -288,6 +289,10 @@ export abstract class Runtime<TConfig extends RuntimeConfig = RuntimeConfig> {
.factory(() => new AnonymousTokenController(this.transport.config))
.scope(Scope.Singleton);

Container.bind(BackboneCompatibilityController)
.factory(() => new BackboneCompatibilityController(this.transport.config))
.scope(Scope.Singleton);

const schemaRepository = new SchemaRepository();
await schemaRepository.loadSchemas();
Container.bind(SchemaRepository)
Expand Down
7 changes: 5 additions & 2 deletions packages/runtime/src/extensibility/AnonymousServices.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { Inject } from "typescript-ioc";
import { AnonymousTokensFacade } from "./facades/anonymous";
import { AnonymousTokensFacade, BackboneCompatibilityFacade } from "./facades/anonymous";

export class AnonymousServices {
public constructor(@Inject public readonly tokens: AnonymousTokensFacade) {}
public constructor(
@Inject public readonly tokens: AnonymousTokensFacade,
@Inject public readonly backboneCompatibility: BackboneCompatibilityFacade
) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Result } from "@js-soft/ts-utils";
import { Inject } from "typescript-ioc";
import { CheckBackboneCompatibilityResponse, CheckBackboneCompatibilityUseCase } from "../../../useCases";

export class BackboneCompatibilityFacade {
public constructor(@Inject private readonly checkBackboneCompatibilityUseCase: CheckBackboneCompatibilityUseCase) {}

public async checkBackboneCompatibility(): Promise<Result<CheckBackboneCompatibilityResponse>> {
return await this.checkBackboneCompatibilityUseCase.execute();
}
}
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./AnonymousTokensFacade";
export * from "./BackboneCompatibilityFacade";
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Result } from "@js-soft/ts-utils";
import { BackboneCompatibilityController } from "@nmshd/transport";
import { Inject } from "typescript-ioc";
import { UseCase } from "../../common";

export interface CheckBackboneCompatibilityResponse {
isCompatible: boolean;
backboneVersion: number;
supportedMinBackboneVersion: number;
supportedMaxBackboneVersion: number;
}

export class CheckBackboneCompatibilityUseCase extends UseCase<void, CheckBackboneCompatibilityResponse> {
public constructor(@Inject private readonly backboneCompatibilityController: BackboneCompatibilityController) {
super();
}

protected async executeInternal(): Promise<Result<CheckBackboneCompatibilityResponse>> {
const result = await this.backboneCompatibilityController.checkBackboneCompatibility();
if (result.isError) return Result.fail(result.error);

return Result.ok(result.value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./CheckBackboneCompatibilityUseCase";
1 change: 1 addition & 0 deletions packages/runtime/src/useCases/anonymous/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./backboneCompatibility";
export * from "./tokens";
69 changes: 69 additions & 0 deletions packages/runtime/test/anonymous/backboneCompatibility.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { NoLoginTestRuntime, RuntimeServiceProvider } from "../lib";

describe("Backbone Compatibility Check", () => {
const serviceProvider = new RuntimeServiceProvider();
let noLoginRuntime: NoLoginTestRuntime;

let actualBackboneVersion: number;

beforeAll(async () => {
const runtime = new NoLoginTestRuntime({ transportLibrary: RuntimeServiceProvider.defaultConfig.transportLibrary, modules: RuntimeServiceProvider.defaultConfig.modules });
await runtime.init();
await runtime.start();

const result = await runtime.anonymousServices.backboneCompatibility.checkBackboneCompatibility();
actualBackboneVersion = result.value.backboneVersion;

await runtime.stop();
});

afterEach(async () => {
await serviceProvider.stop();
await noLoginRuntime.stop();
});

test("should successfully check the compatibility", async () => {
const transportConfigOverride = { supportedMinBackboneVersion: actualBackboneVersion, supportedMaxBackboneVersion: actualBackboneVersion };
noLoginRuntime = new NoLoginTestRuntime({
transportLibrary: { ...RuntimeServiceProvider.defaultConfig.transportLibrary, ...transportConfigOverride },
modules: RuntimeServiceProvider.defaultConfig.modules
});
await noLoginRuntime.init();
await noLoginRuntime.start();

const result = await noLoginRuntime.anonymousServices.backboneCompatibility.checkBackboneCompatibility();

expect(result).toBeSuccessful();
expect(result.value.isCompatible).toBe(true);
});

test("should catch a too low backbone version", async () => {
const transportConfigOverride = { supportedMinBackboneVersion: actualBackboneVersion - 1, supportedMaxBackboneVersion: actualBackboneVersion - 1 };
noLoginRuntime = new NoLoginTestRuntime({
transportLibrary: { ...RuntimeServiceProvider.defaultConfig.transportLibrary, ...transportConfigOverride },
modules: RuntimeServiceProvider.defaultConfig.modules
});
await noLoginRuntime.init();
await noLoginRuntime.start();

const result = await noLoginRuntime.anonymousServices.backboneCompatibility.checkBackboneCompatibility();

expect(result).toBeSuccessful();
expect(result.value.isCompatible).toBe(false);
});

test("should catch a too high backbone version", async () => {
const transportConfigOverride = { supportedMinBackboneVersion: actualBackboneVersion + 1, supportedMaxBackboneVersion: actualBackboneVersion + 1 };
noLoginRuntime = new NoLoginTestRuntime({
transportLibrary: { ...RuntimeServiceProvider.defaultConfig.transportLibrary, ...transportConfigOverride },
modules: RuntimeServiceProvider.defaultConfig.modules
});
await noLoginRuntime.init();
await noLoginRuntime.start();

const result = await noLoginRuntime.anonymousServices.backboneCompatibility.checkBackboneCompatibility();

expect(result).toBeSuccessful();
expect(result.value.isCompatible).toBe(false);
});
});
4 changes: 4 additions & 0 deletions packages/transport/src/core/Transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export interface IConfig {
allowIdentityCreation: boolean;
supportedDatawalletVersion: number;
supportedIdentityVersion: number;
supportedMinBackboneVersion: number;
supportedMaxBackboneVersion: number;
debug: boolean;
platformClientId: string;
platformClientSecret: string;
Expand Down Expand Up @@ -59,6 +61,8 @@ export class Transport {
allowIdentityCreation: true,
supportedDatawalletVersion: 1,
supportedIdentityVersion: -1,
supportedMinBackboneVersion: 6,
supportedMaxBackboneVersion: 6,
debug: false,
platformClientId: "",
platformClientSecret: "",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Result } from "@js-soft/ts-utils";
import { IConfig } from "../../core";
import { VersionClient } from "./backbone/VersionClient";

export interface BackboneCompatibility {
isCompatible: boolean;
backboneVersion: number;
supportedMinBackboneVersion: number;
supportedMaxBackboneVersion: number;
}

export class BackboneCompatibilityController {
private readonly client: VersionClient;

public constructor(private readonly config: IConfig) {
this.client = new VersionClient(config);
this.config = config;
}

public async checkBackboneCompatibility(): Promise<Result<BackboneCompatibility>> {
const getBackboneVersionResult = await this.client.getBackboneVersion();
if (getBackboneVersionResult.isError) return Result.fail(getBackboneVersionResult.error);

const backboneVersion = getBackboneVersionResult.value.majorVersion;
const supportedMinBackboneVersion = this.config.supportedMinBackboneVersion;
const supportedMaxBackboneVersion = this.config.supportedMaxBackboneVersion;
const isCompatible = backboneVersion >= supportedMinBackboneVersion && backboneVersion <= supportedMaxBackboneVersion;

return Result.ok({ isCompatible, backboneVersion, supportedMinBackboneVersion, supportedMaxBackboneVersion });
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface GetBackboneVersionResponse {
majorVersion: number;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { RESTClient } from "../../../core";
import { ClientResult } from "../../../core/backbone/ClientResult";
import { GetBackboneVersionResponse } from "./GetBackboneVersionResponse";

export class VersionClient extends RESTClient {
public async getBackboneVersion(): Promise<ClientResult<GetBackboneVersionResponse>> {
return await this.get<GetBackboneVersionResponse>("/api/v1/Version");
}
}
3 changes: 3 additions & 0 deletions packages/transport/src/modules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ export * from "./accounts/data/IdentityDeletionProcessStatus";
export * from "./accounts/IdentityController";
export * from "./accounts/IdentityDeletionProcessController";
export * from "./accounts/IdentityUtil";
export * from "./backboneCompatibility/backbone/GetBackboneVersionResponse";
export * from "./backboneCompatibility/backbone/VersionClient";
export * from "./backboneCompatibility/BackboneCompatibilityController";
export * from "./certificates/CertificateController";
export * from "./certificates/CertificateIssuer";
export * from "./certificates/CertificateValidator";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { IDatabaseConnection } from "@js-soft/docdb-access-abstractions";
import { BackboneCompatibilityController, Transport } from "../../../src";
import { TestUtil } from "../../testHelpers/TestUtil";

describe("BackboneCompatibility", function () {
let connection: IDatabaseConnection;
let transport: Transport;

let actualBackboneVersion: number;

beforeAll(async function () {
connection = await TestUtil.createDatabaseConnection();
transport = TestUtil.createTransport(connection);

await transport.init();

actualBackboneVersion = (await new BackboneCompatibilityController({ ...transport.config })["client"].getBackboneVersion()).value.majorVersion;
});

afterAll(async function () {
await connection.close();
});

test("should correctly check a valid backbone version with exact same min and max version of the backbone", async function () {
const versionController = new BackboneCompatibilityController({
...transport.config,
supportedMinBackboneVersion: actualBackboneVersion,
supportedMaxBackboneVersion: actualBackboneVersion
});

const versionCheckResult = await versionController.checkBackboneCompatibility();

expect(versionCheckResult.value.isCompatible).toBe(true);
});

test("should correctly check a valid backbone version with version in bounds of min and max", async function () {
const versionController = new BackboneCompatibilityController({
...transport.config,
supportedMinBackboneVersion: actualBackboneVersion - 1,
supportedMaxBackboneVersion: actualBackboneVersion + 1
});

const versionCheckResult = await versionController.checkBackboneCompatibility();

expect(versionCheckResult.value.isCompatible).toBe(true);
});

test("should catch a too low backbone version", async function () {
const versionController = new BackboneCompatibilityController({
...transport.config,
supportedMinBackboneVersion: actualBackboneVersion + 1,
supportedMaxBackboneVersion: actualBackboneVersion + 1
});

const versionCheckResult = await versionController.checkBackboneCompatibility();

expect(versionCheckResult.value.isCompatible).toBe(false);
});

test("should catch a too high backbone version", async function () {
const versionController = new BackboneCompatibilityController({
...transport.config,
supportedMinBackboneVersion: actualBackboneVersion - 1,
supportedMaxBackboneVersion: actualBackboneVersion - 1
});

const versionCheckResult = await versionController.checkBackboneCompatibility();

expect(versionCheckResult.value.isCompatible).toBe(false);
});
});

0 comments on commit 119fd8b

Please sign in to comment.