Skip to content

Commit

Permalink
Personalized Tokens (#294)
Browse files Browse the repository at this point in the history
* feat: add personalization to templates

* refactor/fix: cleanup

* feat: add personalization to tokens

* feat: only string forIdentity

* test: improve tests

* fix: missing forIdentity

* test: personalized token, non-pers template test

* test: refactor comments

* test: refactoring continued

* chore: use speaking content instead of gibberish

---------

Co-authored-by: Julian König <[email protected]>
  • Loading branch information
Magnus-Kuhn and jkoenig134 authored Nov 12, 2024
1 parent f01f59a commit de54d46
Show file tree
Hide file tree
Showing 11 changed files with 86 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export interface CreateTokenForFileRequest {
expiresAt?: string;
ephemeral?: boolean;
forIdentity?: string;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export interface CreateTokenQrCodeForFileRequest {
expiresAt?: string;
forIdentity?: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export interface CreateOwnTokenRequest {
expiresAt: string;
content: unknown;
ephemeral?: boolean;
forIdentity?: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export interface GetOwnTokensRequest {
createdAt?: string | string[];
createdByDevice?: string | string[];
expiresAt?: string | string[];
forIdentity?: string | string[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export interface GetPeerTokensRequest {
createdAt?: string | string[];
createdBy?: string | string[];
expiresAt?: string | string[];
forIdentity?: string;
}
6 changes: 4 additions & 2 deletions src/modules/coreHttpApi/controllers/FilesController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ export class FilesController extends BaseController {
case "image/png":
const qrCodeResult = await this.transportServices.files.createTokenQRCodeForFile({
fileId: id,
expiresAt: request.expiresAt
expiresAt: request.expiresAt,
forIdentity: request.forIdentity
});
return this.file(
qrCodeResult,
Expand All @@ -140,7 +141,8 @@ export class FilesController extends BaseController {
const jsonResult = await this.transportServices.files.createTokenForFile({
fileId: id,
expiresAt: request.expiresAt,
ephemeral: request.ephemeral
ephemeral: request.ephemeral,
forIdentity: request.forIdentity
});
return this.created(jsonResult);
}
Expand Down
16 changes: 16 additions & 0 deletions src/modules/coreHttpApi/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2126,6 +2126,10 @@ paths:
ephemeral:
description: If set to true the token will will not be cached in the database of the connector. Note that you will not be able to fetch this token unless you remember the truncatedReference of the token. Defaults to true. Will be ignored if Accept is set to image/png.
type: boolean
forIdentity:
$ref: "#/components/schemas/Address"
nullable: true
description: The only Identity that may load this Token.
responses:
201:
description: Success
Expand Down Expand Up @@ -4119,6 +4123,10 @@ paths:
description: If set to true the token will will not be cached in the database of the connector. Note that you will not be able to fetch this token unless you remember the truncatedReference of the token. Defaults to false.
type: boolean
example: false
forIdentity:
$ref: "#/components/schemas/Address"
nullable: true
description: The only Identity that may load this Token.
required:
- content
- expiresAt
Expand Down Expand Up @@ -4167,6 +4175,10 @@ paths:
name: expiresAt
schema:
$ref: "#/components/schemas/DateFilter"
- in: query
name: forIdentity
schema:
$ref: "#/components/schemas/IdFilter"
responses:
200:
description: Success
Expand Down Expand Up @@ -4266,6 +4278,10 @@ paths:
name: expiresAt
schema:
$ref: "#/components/schemas/DateFilter"
- in: query
name: forIdentity
schema:
$ref: "#/components/schemas/IdFilter"
responses:
200:
description: Success
Expand Down
9 changes: 9 additions & 0 deletions test/files.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,15 @@ describe("Load peer file with token reference", () => {
expect(response.result).toContainEqual({ ...file, isOwn: false });
});

test("token can be personalized", async () => {
const client2address = (await client2.account.getIdentityInfo()).result.address;
const token = (await client1.files.createTokenForFile(file.id, { forIdentity: client2address })).result;
expect(token.forIdentity).toBe(client2address);

const response = await client2.files.loadPeerFile({ reference: token.truncatedReference });
expect(response).toBeSuccessful(ValidationSchema.File);
});

test("passing token id as truncated token reference causes an error", async () => {
const file = await uploadFile(client1);
const token = (await client1.files.createTokenForFile(file.id)).result;
Expand Down
13 changes: 6 additions & 7 deletions test/lib/testUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,11 @@ export async function syncUntilHasMessageWithResponse(client: ConnectorClientWit
).data.message;
}

export async function uploadOwnToken(client: ConnectorClient): Promise<ConnectorToken> {
export async function uploadOwnToken(client: ConnectorClient, forIdentity?: string): Promise<ConnectorToken> {
const response = await client.tokens.createOwnToken({
content: {
content: "Hello"
},
expiresAt: DateTime.utc().plus({ days: 1 }).toString()
content: { aKey: "aValue" },
expiresAt: DateTime.utc().plus({ days: 1 }).toString(),
forIdentity
});

expect(response).toBeSuccessful(ValidationSchema.Token);
Expand Down Expand Up @@ -233,8 +232,8 @@ export async function exchangeFile(clientCreator: ConnectorClient, clientRecpipi
return response.result;
}

export async function exchangeToken(clientCreator: ConnectorClient, clientRecpipient: ConnectorClient): Promise<ConnectorToken> {
const token = await uploadOwnToken(clientCreator);
export async function exchangeToken(clientCreator: ConnectorClient, clientRecpipient: ConnectorClient, forIdentity?: string): Promise<ConnectorToken> {
const token = await uploadOwnToken(clientCreator, forIdentity);

const response = await clientRecpipient.tokens.loadPeerToken({ reference: token.truncatedReference });
expect(response).toBeSuccessful(ValidationSchema.Token);
Expand Down
12 changes: 12 additions & 0 deletions test/relationshipTemplates.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,18 @@ describe("Template Tests", () => {
expect(response).toBeSuccessful(ValidationSchema.RelationshipTemplate);
expect(response.result.forIdentity).toBe(client2address);
});

test("send and receive a non-personalized template via personalized token", async () => {
const client2address = (await client2.account.getIdentityInfo()).result.address;
const template = await createTemplate(client1, client2address);
const token = (await client1.relationshipTemplates.createTokenForOwnRelationshipTemplate(template.id, { forIdentity: client2address })).result;

const response = await client2.relationshipTemplates.loadPeerRelationshipTemplate({
reference: token.truncatedReference
});
expect(response).toBeSuccessful(ValidationSchema.RelationshipTemplate);
expect(response.result.forIdentity).toBe(client2address);
});
});

describe("Serialization Errors", () => {
Expand Down
38 changes: 34 additions & 4 deletions test/tokens.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ConnectorClient } from "@nmshd/connector-sdk";
import { DateTime } from "luxon";
import { Launcher } from "./lib/Launcher";
import { QueryParamConditions } from "./lib/QueryParamConditions";
import { getTimeout } from "./lib/setTimeout";
Expand All @@ -12,16 +13,45 @@ let client2: ConnectorClient;
beforeAll(async () => ([client1, client2] = await launcher.launch(2)), getTimeout(30000));
afterAll(() => launcher.stop());

test("send a token", async () => {
const response = await client1.tokens.createOwnToken({
content: { aKey: "aValue" },
expiresAt: DateTime.utc().plus({ days: 1 }).toString()
});
expect(response).toBeSuccessful(ValidationSchema.Token);
});

test("load a token", async () => {
const template = await uploadOwnToken(client1);

const response = await client2.tokens.loadPeerToken({
reference: template.truncatedReference
});
expect(response).toBeSuccessful(ValidationSchema.Token);
});

test("send and receive a personalized token", async () => {
const client2address = (await client2.account.getIdentityInfo()).result.address;
const template = await uploadOwnToken(client1, client2address);
expect(template.forIdentity).toBe(client2address);

const response = await client2.tokens.loadPeerToken({
reference: template.truncatedReference
});
expect(response).toBeSuccessful(ValidationSchema.Token);
expect(response.result.forIdentity).toBe(client2address);
});

describe("Tokens query", () => {
test("query own tokens", async () => {
const token = await uploadOwnToken(client1);
const conditions = new QueryParamConditions(token, client1).addDateSet("expiresAt").addDateSet("createdAt").addStringSet("createdByDevice");
const token = await uploadOwnToken(client1, (await client1.account.getIdentityInfo()).result.address);
const conditions = new QueryParamConditions(token, client1).addDateSet("expiresAt").addDateSet("createdAt").addStringSet("createdByDevice").addStringSet("forIdentity");
await conditions.executeTests((c, q) => c.tokens.getOwnTokens(q), ValidationSchema.Tokens);
});

test("query peer tokens", async () => {
const token = await exchangeToken(client1, client2);
const conditions = new QueryParamConditions(token, client2).addDateSet("expiresAt").addDateSet("createdAt").addStringSet("createdBy");
const token = await exchangeToken(client1, client2, (await client2.account.getIdentityInfo()).result.address);
const conditions = new QueryParamConditions(token, client2).addDateSet("expiresAt").addDateSet("createdAt").addStringSet("createdBy").addStringSet("forIdentity");
await conditions.executeTests((c, q) => c.tokens.getPeerTokens(q), ValidationSchema.Tokens);
});
});

0 comments on commit de54d46

Please sign in to comment.