From 59d5579c4b9996bfebd09cdd0ca12919a6bb8d08 Mon Sep 17 00:00:00 2001 From: Magnus Kuhn <127854942+Magnus-Kuhn@users.noreply.github.com> Date: Fri, 13 Dec 2024 13:26:21 +0100 Subject: [PATCH] Query Tokens for password protection (#362) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .../runtime/src/useCases/common/Schemas.ts | 27 +++++++++ .../useCases/transport/tokens/GetTokens.ts | 28 ++++++++- .../runtime/test/transport/tokens.test.ts | 57 ++++++++++++++++++- 3 files changed, 106 insertions(+), 6 deletions(-) diff --git a/packages/runtime/src/useCases/common/Schemas.ts b/packages/runtime/src/useCases/common/Schemas.ts index 312b3b42d..78b8b303c 100644 --- a/packages/runtime/src/useCases/common/Schemas.ts +++ b/packages/runtime/src/useCases/common/Schemas.ts @@ -23071,6 +23071,33 @@ export const GetTokensRequest: any = { } } ] + }, + "passwordProtection": { + "type": "string", + "enum": [ + "", + "!" + ] + }, + "passwordProtection.password": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "passwordProtection.passwordIsPin": { + "type": "string", + "enum": [ + "true", + "!" + ] } }, "additionalProperties": false diff --git a/packages/runtime/src/useCases/transport/tokens/GetTokens.ts b/packages/runtime/src/useCases/transport/tokens/GetTokens.ts index f48bd7650..c77494253 100644 --- a/packages/runtime/src/useCases/transport/tokens/GetTokens.ts +++ b/packages/runtime/src/useCases/transport/tokens/GetTokens.ts @@ -1,6 +1,6 @@ import { QueryTranslator } from "@js-soft/docdb-querytranslator"; import { Result } from "@js-soft/ts-utils"; -import { CachedToken, Token, TokenController } from "@nmshd/transport"; +import { CachedToken, PasswordProtection, Token, TokenController } from "@nmshd/transport"; import { Inject } from "@nmshd/typescript-ioc"; import { nameof } from "ts-simple-nameof"; import { TokenDTO } from "../../../types"; @@ -13,6 +13,9 @@ export interface GetTokensQuery { createdByDevice?: string | string[]; expiresAt?: string | string[]; forIdentity?: string | string[]; + passwordProtection?: "" | "!"; + "passwordProtection.password"?: string | string[]; + "passwordProtection.passwordIsPin"?: "true" | "!"; } export interface GetTokensRequest { @@ -33,14 +36,33 @@ export class GetTokensUseCase extends UseCase { [nameof((t) => t.createdBy)]: true, [nameof((t) => t.createdByDevice)]: true, [nameof((t) => t.expiresAt)]: true, - [nameof((t) => t.forIdentity)]: true + [nameof((t) => t.forIdentity)]: true, + [nameof((r) => r.passwordProtection)]: true, + [`${nameof((r) => r.passwordProtection)}.password`]: true, + [`${nameof((r) => r.passwordProtection)}.passwordIsPin`]: true }, alias: { [nameof((t) => t.createdAt)]: `${nameof((t) => t.cache)}.${[nameof((t) => t.createdAt)]}`, [nameof((t) => t.createdBy)]: `${nameof((t) => t.cache)}.${[nameof((t) => t.createdBy)]}`, [nameof((t) => t.createdByDevice)]: `${nameof((t) => t.cache)}.${[nameof((t) => t.createdByDevice)]}`, [nameof((t) => t.expiresAt)]: `${nameof((t) => t.cache)}.${[nameof((t) => t.expiresAt)]}`, - [nameof((t) => t.forIdentity)]: `${nameof((t) => t.cache)}.${[nameof((t) => t.forIdentity)]}` + [nameof((t) => t.forIdentity)]: `${nameof((t) => t.cache)}.${[nameof((t) => t.forIdentity)]}`, + [nameof((r) => r.passwordProtection)]: nameof((r) => r.passwordProtection) + }, + custom: { + [`${nameof((r) => r.passwordProtection)}.password`]: (query: any, input: string) => { + query[`${nameof((t) => t.passwordProtection)}.${nameof((t) => t.password)}`] = input; + }, + [`${nameof((t) => t.passwordProtection)}.passwordIsPin`]: (query: any, input: string) => { + if (input === "true") { + query[`${nameof((t) => t.passwordProtection)}.${nameof((t) => t.passwordType)}`] = { + $regex: "^pin" + }; + } + if (input === "!") { + query[`${nameof((t) => t.passwordProtection)}.${nameof((t) => t.passwordType)}`] = "pw"; + } + } } }); diff --git a/packages/runtime/test/transport/tokens.test.ts b/packages/runtime/test/transport/tokens.test.ts index 0e1a0dd99..482b7f4ce 100644 --- a/packages/runtime/test/transport/tokens.test.ts +++ b/packages/runtime/test/transport/tokens.test.ts @@ -50,18 +50,69 @@ describe("Tokens errors", () => { describe("Tokens query", () => { test("query own tokens", async () => { - const token = await uploadOwnToken(runtimeServices1.transport, runtimeServices1.address); + const token = await uploadOwnToken(runtimeServices1.transport, runtimeServices1.address, { password: "password" }); const conditions = new QueryParamConditions(token, runtimeServices1.transport) .addDateSet("expiresAt") .addDateSet("createdAt") .addStringSet("createdByDevice") - .addStringSet("forIdentity"); + .addStringSet("forIdentity") + .addSingleCondition({ + expectedResult: true, + key: "passwordProtection", + value: "" + }) + .addSingleCondition({ + expectedResult: false, + key: "passwordProtection", + value: "!" + }) + .addStringSet("passwordProtection.password") + .addSingleCondition({ + expectedResult: false, + key: "passwordProtection.passwordIsPin", + value: "true" + }) + .addSingleCondition({ + expectedResult: true, + key: "passwordProtection.passwordIsPin", + value: "!" + }); + await conditions.executeTests((c, q) => c.tokens.getTokens({ query: q, ownerRestriction: OwnerRestriction.Own })); + }); + + test("query own PIN-protected tokens", async () => { + const token = await uploadOwnToken(runtimeServices1.transport, runtimeServices1.address, { password: "1234", passwordIsPin: true }); + const conditions = new QueryParamConditions(token, runtimeServices1.transport) + .addStringSet("passwordProtection.password") + .addSingleCondition({ + expectedResult: true, + key: "passwordProtection.passwordIsPin", + value: "true" + }) + .addSingleCondition({ + expectedResult: false, + key: "passwordProtection.passwordIsPin", + value: "!" + }); await conditions.executeTests((c, q) => c.tokens.getTokens({ query: q, ownerRestriction: OwnerRestriction.Own })); }); test("query peer tokens", async () => { const token = await exchangeToken(runtimeServices1.transport, runtimeServices2.transport); - const conditions = new QueryParamConditions(token, runtimeServices2.transport).addDateSet("expiresAt").addDateSet("createdAt").addStringSet("createdBy"); + const conditions = new QueryParamConditions(token, runtimeServices2.transport) + .addDateSet("expiresAt") + .addDateSet("createdAt") + .addStringSet("createdBy") + .addSingleCondition({ + expectedResult: false, + key: "passwordProtection", + value: "" + }) + .addSingleCondition({ + expectedResult: true, + key: "passwordProtection", + value: "!" + }); await conditions.executeTests((c, q) => c.tokens.getTokens({ query: q, ownerRestriction: OwnerRestriction.Peer })); }); });