Skip to content

Commit 20398db

Browse files
RuthDiGmergify[bot]jkoenig134britsta
authored
Block sending Messages to Identities which are in deletion or for which the Relationship is terminated (#286)
* feat: add validation to incoming- outgoing and messageController * fix: relationships.test * fix: Notification shouldn't be blocked by the Runtime * fix: change place of validation * refactor: incorporate code review * feat: provide list of failing addresses and other changes * fix: add validation regarding RelationshipTermination * fix: refactoring of MessageController etc. * feat: make error more explicitly * chore: test without enabling modules * fix: add validation in case of Request * chore: incorporate code Review * chore: change naming etc * refactor: MessageController and errorMessages in IncomingRequestsController.test * fix: change Test setting * refactor: MessageController * chore: incorporate code review * refactor: comments * refactor: use PeerDeletionStatus * feat: add new test * fix: change wrong test * refactor: error and add some tests * feat: add test for Attributssuccession * fix: add waiting for event * refactor: toBeDeleted in IsInDeletion * refactor: wording * test: use helper methods instead of re-inventing the wheel * refactor: make code readable using helper methods * refactor: check if only one recipient early if the content is a request * fix: throw error instead of result * feat: add tests * refactor: change place of testing * chore: remove comments * chore: remove other comment * feat: move tests to notifications.test * feat: change place for test and avoid enabling modules * refactor: move validation block of canDecide method of IncomingRequestsController * refactor: place related tests together * fix: failing tests due to an error thrown elsewhere * refactor: be consistent with written out error messages * fix: wrong error code in tests * refactor: make use of auxiliary functions and only necessary expect statements * refactor: remove redundant tests * refactor: move tests to appropriate file * refactor: move tests to right location * chore: reduce file diffs * chore: remove empty line * refactor: use descriptive names and change error order * refactor: change order of thrown errors * refactor: change order of error messages * refactor: simplify errors and define them in appropriate library * fix: grammatical error * fix: failing tests due to strange bracket comparison * test: sending Messages requires existing of active or terminated Relationship * fix: tests on a terminated Relationship * refactor: tests for sending Messages with recipient in deletion * test: more precise test names * refactor: emphasize key of error messages * test: remove redundant tests * test: make use of auxiliary function * fix: missing bracket * chore: remove unneccessary empty lines * fix: failing test due to special error format of Transport library * fix: prettier error * chore: add empty line * fix: failing test due to special error format of Transport library * fix: failing test due to missing sync * refactor: make variable constant * refactor: undo last commit --------- Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Julian König <[email protected]> Co-authored-by: Britta Stallknecht <[email protected]> Co-authored-by: Britta Stallknecht <[email protected]>
1 parent 6718203 commit 20398db

File tree

17 files changed

+1073
-224
lines changed

17 files changed

+1073
-224
lines changed

packages/consumption/src/consumption/ConsumptionCoreErrors.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,14 @@ class Requests {
316316
return new CoreError("error.consumption.requests.missingRelationship", message);
317317
}
318318

319+
public peerIsDeleted(message: string) {
320+
return new CoreError("error.consumption.requests.peerIsDeleted", message);
321+
}
322+
323+
public peerIsInDeletion(message: string) {
324+
return new CoreError("error.consumption.requests.peerIsInDeletion", message);
325+
}
326+
319327
public inheritedFromItem(message: string) {
320328
return new ApplicationError("error.consumption.requests.validation.inheritedFromItem", message);
321329
}

packages/consumption/src/modules/requests/incoming/IncomingRequestsController.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { ServalError } from "@js-soft/ts-serval";
22
import { EventBus } from "@js-soft/ts-utils";
33
import { RequestItem, RequestItemGroup, Response, ResponseItemDerivations, ResponseItemGroup, ResponseResult } from "@nmshd/content";
44
import { CoreAddress, CoreDate, CoreId, ICoreAddress, ICoreId } from "@nmshd/core-types";
5-
import { Message, Relationship, RelationshipStatus, RelationshipTemplate, SynchronizedCollection, TransportCoreErrors } from "@nmshd/transport";
5+
import { Message, PeerDeletionStatus, Relationship, RelationshipStatus, RelationshipTemplate, SynchronizedCollection, TransportCoreErrors } from "@nmshd/transport";
66
import { ConsumptionBaseController } from "../../../consumption/ConsumptionBaseController";
77
import { ConsumptionController } from "../../../consumption/ConsumptionController";
88
import { ConsumptionControllerName } from "../../../consumption/ConsumptionControllerName";
@@ -193,6 +193,18 @@ export class IncomingRequestsController extends ConsumptionBaseController {
193193

194194
this.assertRequestStatus(request, LocalRequestStatus.DecisionRequired, LocalRequestStatus.ManualDecisionRequired);
195195

196+
if (relationship?.peerDeletionInfo?.deletionStatus === PeerDeletionStatus.ToBeDeleted) {
197+
return ValidationResult.error(
198+
ConsumptionCoreErrors.requests.peerIsInDeletion(`You cannot decide a Request from peer '${request.peer.toString()}' since the peer is in deletion.`)
199+
);
200+
}
201+
202+
if (relationship?.peerDeletionInfo?.deletionStatus === PeerDeletionStatus.Deleted) {
203+
return ValidationResult.error(
204+
ConsumptionCoreErrors.requests.peerIsDeleted(`You cannot decide a Request from peer '${request.peer.toString()}' since the peer is deleted.`)
205+
);
206+
}
207+
196208
const validationResult = this.decideRequestParamsValidator.validate(params, request);
197209
if (validationResult.isError()) return validationResult;
198210

packages/consumption/src/modules/requests/outgoing/OutgoingRequestsController.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { EventBus } from "@js-soft/ts-utils";
22
import { DeleteAttributeRequestItem, RelationshipTemplateContent, Request, RequestItem, RequestItemGroup, Response, ResponseItem, ResponseItemGroup } from "@nmshd/content";
33
import { CoreAddress, CoreDate, CoreId, ICoreId } from "@nmshd/core-types";
4-
import { Message, Relationship, RelationshipStatus, RelationshipTemplate, SynchronizedCollection, TransportCoreErrors } from "@nmshd/transport";
4+
import { Message, PeerDeletionStatus, Relationship, RelationshipStatus, RelationshipTemplate, SynchronizedCollection, TransportCoreErrors } from "@nmshd/transport";
55
import { ConsumptionBaseController } from "../../../consumption/ConsumptionBaseController";
66
import { ConsumptionController } from "../../../consumption/ConsumptionController";
77
import { ConsumptionControllerName } from "../../../consumption/ConsumptionControllerName";
@@ -62,6 +62,18 @@ export class OutgoingRequestsController extends ConsumptionBaseController {
6262
)
6363
);
6464
}
65+
66+
if (relationship.peerDeletionInfo?.deletionStatus === PeerDeletionStatus.ToBeDeleted) {
67+
return ValidationResult.error(
68+
ConsumptionCoreErrors.requests.peerIsInDeletion(`You cannot create a Request to peer '${parsedParams.peer.toString()}' since the peer is in deletion.`)
69+
);
70+
}
71+
72+
if (relationship.peerDeletionInfo?.deletionStatus === PeerDeletionStatus.Deleted) {
73+
return ValidationResult.error(
74+
ConsumptionCoreErrors.requests.peerIsDeleted(`You cannot create a Request to peer '${parsedParams.peer.toString()}' since the peer is deleted.`)
75+
);
76+
}
6577
}
6678

6779
const innerResults = await this.canCreateItems(parsedParams.content, parsedParams.peer);

packages/consumption/test/modules/requests/IncomingRequestsController.test.ts

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,8 @@ describe("IncomingRequestsController", function () {
413413
await Given.anIncomingRequestInStatus(LocalRequestStatus.DecisionRequired);
414414
const validationResult = await When.iCallCanAccept();
415415
expect(validationResult).errorValidationResult({
416-
code: "error.consumption.requests.wrongRelationshipStatus"
416+
code: "error.consumption.requests.wrongRelationshipStatus",
417+
message: "You cannot decide a request from 'did:e:a-domain:dids:anidentity' since the relationship is in status 'Terminated'."
417418
});
418419
});
419420

@@ -422,7 +423,28 @@ describe("IncomingRequestsController", function () {
422423
await Given.anIncomingRequestInStatus(LocalRequestStatus.DecisionRequired);
423424
const validationResult = await When.iCallCanAccept();
424425
expect(validationResult).errorValidationResult({
425-
code: "error.consumption.requests.wrongRelationshipStatus"
426+
code: "error.consumption.requests.wrongRelationshipStatus",
427+
message: "You cannot decide a request from 'did:e:a-domain:dids:anidentity' since the relationship is in status 'DeletionProposed'."
428+
});
429+
});
430+
431+
test("returns 'error' on relationship with peer which is in deletion", async function () {
432+
await Given.aRelationshipToPeerInDeletion();
433+
await Given.anIncomingRequestInStatus(LocalRequestStatus.DecisionRequired);
434+
const validationResult = await When.iCallCanAccept();
435+
expect(validationResult).errorValidationResult({
436+
code: "error.consumption.requests.peerIsInDeletion",
437+
message: "You cannot decide a Request from peer 'did:e:a-domain:dids:anidentity' since the peer is in deletion."
438+
});
439+
});
440+
441+
test("returns 'error' on relationship with peer which is deleted", async function () {
442+
await Given.aRelationshipToDeletedPeer();
443+
await Given.anIncomingRequestInStatus(LocalRequestStatus.DecisionRequired);
444+
const validationResult = await When.iCallCanAccept();
445+
expect(validationResult).errorValidationResult({
446+
code: "error.consumption.requests.wrongRelationshipStatus",
447+
message: "You cannot decide a request from 'did:e:a-domain:dids:anidentity' since the relationship is in status 'DeletionProposed'."
426448
});
427449
});
428450
});
@@ -607,7 +629,8 @@ describe("IncomingRequestsController", function () {
607629
await Given.anIncomingRequestInStatus(LocalRequestStatus.DecisionRequired);
608630
const validationResult = await When.iCallCanReject();
609631
expect(validationResult).errorValidationResult({
610-
code: "error.consumption.requests.wrongRelationshipStatus"
632+
code: "error.consumption.requests.wrongRelationshipStatus",
633+
message: "You cannot decide a request from 'did:e:a-domain:dids:anidentity' since the relationship is in status 'Terminated'."
611634
});
612635
});
613636

@@ -616,7 +639,28 @@ describe("IncomingRequestsController", function () {
616639
await Given.anIncomingRequestInStatus(LocalRequestStatus.DecisionRequired);
617640
const validationResult = await When.iCallCanReject();
618641
expect(validationResult).errorValidationResult({
619-
code: "error.consumption.requests.wrongRelationshipStatus"
642+
code: "error.consumption.requests.wrongRelationshipStatus",
643+
message: "You cannot decide a request from 'did:e:a-domain:dids:anidentity' since the relationship is in status 'DeletionProposed'."
644+
});
645+
});
646+
647+
test("returns 'error' on relationship with peer which is in deletion", async function () {
648+
await Given.aRelationshipToPeerInDeletion();
649+
await Given.anIncomingRequestInStatus(LocalRequestStatus.DecisionRequired);
650+
const validationResult = await When.iCallCanReject();
651+
expect(validationResult).errorValidationResult({
652+
code: "error.consumption.requests.peerIsInDeletion",
653+
message: "You cannot decide a Request from peer 'did:e:a-domain:dids:anidentity' since the peer is in deletion."
654+
});
655+
});
656+
657+
test("returns 'error' on relationship with peer which is deleted", async function () {
658+
await Given.aRelationshipToDeletedPeer();
659+
await Given.anIncomingRequestInStatus(LocalRequestStatus.DecisionRequired);
660+
const validationResult = await When.iCallCanReject();
661+
expect(validationResult).errorValidationResult({
662+
code: "error.consumption.requests.wrongRelationshipStatus",
663+
message: "You cannot decide a request from 'did:e:a-domain:dids:anidentity' since the relationship is in status 'DeletionProposed'."
620664
});
621665
});
622666
});

packages/consumption/test/modules/requests/OutgoingRequestsController.test.ts

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,67 @@ describe("OutgoingRequestsController", function () {
226226
});
227227
});
228228

229+
describe("CanCreate (on terminated relationship)", function () {
230+
test("returns a validation result that contains an error if the relationship is terminated", async function () {
231+
await Given.aTerminatedRelationshipToIdentity();
232+
const validationResult = await When.iCallCanCreateForAnOutgoingRequest({
233+
content: {
234+
items: [
235+
TestRequestItem.from({
236+
mustBeAccepted: false,
237+
shouldFailAtCanCreateOutgoingRequestItem: true
238+
})
239+
]
240+
}
241+
});
242+
243+
expect(validationResult).errorValidationResult({
244+
code: "error.consumption.requests.wrongRelationshipStatus",
245+
message: "You cannot create a request to 'did:e:a-domain:dids:anidentity' since the relationship is in status 'Terminated'"
246+
});
247+
});
248+
});
249+
250+
describe("CanCreate (with peer in deletion or deleted peer)", function () {
251+
test("returns a validation result that contains an error for requests to a peer which is in deletion", async function () {
252+
await Given.aRelationshipToPeerInDeletion();
253+
const validationResult = await When.iCallCanCreateForAnOutgoingRequest({
254+
content: {
255+
items: [
256+
TestRequestItem.from({
257+
mustBeAccepted: false,
258+
shouldFailAtCanCreateOutgoingRequestItem: true
259+
})
260+
]
261+
}
262+
});
263+
264+
expect(validationResult).errorValidationResult({
265+
code: "error.consumption.requests.peerIsInDeletion",
266+
message: "You cannot create a Request to peer 'did:e:a-domain:dids:anidentity' since the peer is in deletion."
267+
});
268+
});
269+
270+
test("returns a validation result that contains an error for requests to a peer which is deleted", async function () {
271+
await Given.aRelationshipToDeletedPeer();
272+
const validationResult = await When.iCallCanCreateForAnOutgoingRequest({
273+
content: {
274+
items: [
275+
TestRequestItem.from({
276+
mustBeAccepted: false,
277+
shouldFailAtCanCreateOutgoingRequestItem: true
278+
})
279+
]
280+
}
281+
});
282+
283+
expect(validationResult).errorValidationResult({
284+
code: "error.consumption.requests.wrongRelationshipStatus",
285+
message: "You cannot create a request to 'did:e:a-domain:dids:anidentity' since the relationship is in status 'DeletionProposed'."
286+
});
287+
});
288+
});
289+
229290
describe("Create (on active relationship)", function () {
230291
beforeEach(async function () {
231292
await Given.anActiveRelationshipToIdentity();
@@ -804,23 +865,4 @@ describe("OutgoingRequestsController", function () {
804865
await Then.itThrowsAnErrorWithTheErrorMessage("*Local Request has to be in status 'Draft'*");
805866
});
806867
});
807-
808-
describe("CanCreate (on terminated relationship)", function () {
809-
test("returns 'error' when the relationship is terminated", async function () {
810-
await Given.aTerminatedRelationshipToIdentity();
811-
const validationResult = await When.iCallCanCreateForAnOutgoingRequest({
812-
content: {
813-
items: [
814-
TestRequestItem.from({
815-
mustBeAccepted: false,
816-
shouldFailAtCanCreateOutgoingRequestItem: true
817-
})
818-
]
819-
}
820-
});
821-
expect(validationResult).errorValidationResult({
822-
code: "error.consumption.requests.wrongRelationshipStatus"
823-
});
824-
});
825-
});
826868
});

packages/consumption/test/modules/requests/RequestsIntegrationTest.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,18 @@ export class RequestsGiven {
176176
return Promise.resolve();
177177
}
178178

179+
public aRelationshipToPeerInDeletion(): Promise<void> {
180+
this.context.relationshipToReturnFromGetRelationshipToIdentity = TestObjectFactory.createRelationshipToPeerInDeletion();
181+
182+
return Promise.resolve();
183+
}
184+
185+
public aRelationshipToDeletedPeer(): Promise<void> {
186+
this.context.relationshipToReturnFromGetRelationshipToIdentity = TestObjectFactory.createRelationshipToDeletedPeer();
187+
188+
return Promise.resolve();
189+
}
190+
179191
public aDeletionProposedRelationshipToIdentity(): Promise<void> {
180192
this.context.relationshipToReturnFromGetRelationshipToIdentity = TestObjectFactory.createDeletionProposedRelationship();
181193

packages/consumption/test/modules/requests/testHelpers/TestObjectFactory.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import {
2828
IRelationship,
2929
IRelationshipTemplate,
3030
Message,
31+
PeerDeletionInfo,
32+
PeerDeletionStatus,
3133
Relationship,
3234
RelationshipAuditLogEntryReason,
3335
RelationshipStatus,
@@ -164,6 +166,112 @@ export class TestObjectFactory {
164166
});
165167
}
166168

169+
public static createRelationshipToPeerInDeletion(properties?: Partial<IRelationship>): Relationship {
170+
return Relationship.from({
171+
id: properties?.id ?? CoreId.from("REL1"),
172+
peer:
173+
properties?.peer ??
174+
Identity.from({
175+
address: CoreAddress.from("did:e:a-domain:dids:anidentity"),
176+
publicKey: CryptoSignaturePublicKey.from({
177+
algorithm: CryptoSignatureAlgorithm.ECDSA_ED25519,
178+
publicKey: CoreBuffer.from("L1sPFQgS5CxgGs1ejBcWCQLCpeFXbRc1TQnSpuHQqDQ")
179+
})
180+
}),
181+
peerDeletionInfo:
182+
properties?.peerDeletionInfo ??
183+
PeerDeletionInfo.from({
184+
deletionStatus: PeerDeletionStatus.ToBeDeleted,
185+
deletionDate: CoreDate.from("2100-01-03T00:00:00.000Z")
186+
}),
187+
status: properties?.status ?? RelationshipStatus.Active,
188+
relationshipSecretId: properties?.relationshipSecretId ?? CoreId.from("RELSEC1"),
189+
cachedAt: properties?.cachedAt ?? CoreDate.from("2020-01-03T00:00:00.000Z"),
190+
cache:
191+
properties?.cache ??
192+
CachedRelationship.from({
193+
creationContent: {},
194+
auditLog: [
195+
{
196+
createdAt: CoreDate.from("2020-01-01T00:00:00.000Z"),
197+
createdBy: CoreAddress.from("did:e:a-domain:dids:anidentity2"),
198+
createdByDevice: CoreId.from("DVC1"),
199+
reason: RelationshipAuditLogEntryReason.Creation,
200+
newStatus: RelationshipStatus.Pending
201+
},
202+
203+
{
204+
createdAt: CoreDate.from("2020-01-02T00:00:00.000Z"),
205+
createdBy: CoreAddress.from("did:e:a-domain:dids:anidentity"),
206+
createdByDevice: CoreId.from("DVC1"),
207+
reason: RelationshipAuditLogEntryReason.AcceptanceOfCreation,
208+
oldStatus: RelationshipStatus.Pending,
209+
newStatus: RelationshipStatus.Active
210+
}
211+
],
212+
template: this.createIncomingRelationshipTemplate()
213+
})
214+
});
215+
}
216+
217+
public static createRelationshipToDeletedPeer(properties?: Partial<IRelationship>): Relationship {
218+
return Relationship.from({
219+
id: properties?.id ?? CoreId.from("REL1"),
220+
peer:
221+
properties?.peer ??
222+
Identity.from({
223+
address: CoreAddress.from("did:e:a-domain:dids:anidentity"),
224+
publicKey: CryptoSignaturePublicKey.from({
225+
algorithm: CryptoSignatureAlgorithm.ECDSA_ED25519,
226+
publicKey: CoreBuffer.from("L1sPFQgS5CxgGs1ejBcWCQLCpeFXbRc1TQnSpuHQqDQ")
227+
})
228+
}),
229+
peerDeletionInfo:
230+
properties?.peerDeletionInfo ??
231+
PeerDeletionInfo.from({
232+
deletionStatus: PeerDeletionStatus.Deleted,
233+
deletionDate: CoreDate.from("2022-01-03T00:00:00.000Z")
234+
}),
235+
status: properties?.status ?? RelationshipStatus.DeletionProposed,
236+
relationshipSecretId: properties?.relationshipSecretId ?? CoreId.from("RELSEC1"),
237+
cachedAt: properties?.cachedAt ?? CoreDate.from("2022-01-03T00:00:00.000Z"),
238+
cache:
239+
properties?.cache ??
240+
CachedRelationship.from({
241+
creationContent: {},
242+
auditLog: [
243+
{
244+
createdAt: CoreDate.from("2020-01-01T00:00:00.000Z"),
245+
createdBy: CoreAddress.from("did:e:a-domain:dids:anidentity2"),
246+
createdByDevice: CoreId.from("DVC1"),
247+
reason: RelationshipAuditLogEntryReason.Creation,
248+
newStatus: RelationshipStatus.Pending
249+
},
250+
251+
{
252+
createdAt: CoreDate.from("2020-01-02T00:00:00.000Z"),
253+
createdBy: CoreAddress.from("did:e:a-domain:dids:anidentity"),
254+
createdByDevice: CoreId.from("DVC1"),
255+
reason: RelationshipAuditLogEntryReason.AcceptanceOfCreation,
256+
oldStatus: RelationshipStatus.Pending,
257+
newStatus: RelationshipStatus.Active
258+
},
259+
260+
{
261+
createdAt: CoreDate.from("2022-01-03T00:00:00.000Z"),
262+
createdBy: CoreAddress.from("did:e:a-domain:dids:anidentity"),
263+
createdByDevice: CoreId.from("DVC1"),
264+
// must be DecompositionDueToIdentityDeletion in the future
265+
reason: RelationshipAuditLogEntryReason.Decomposition,
266+
oldStatus: RelationshipStatus.Active,
267+
newStatus: RelationshipStatus.DeletionProposed
268+
}
269+
],
270+
template: this.createIncomingRelationshipTemplate()
271+
})
272+
});
273+
}
274+
167275
public static createDeletionProposedRelationship(properties?: Partial<IRelationship>): Relationship {
168276
return Relationship.from({
169277
id: properties?.id ?? CoreId.from("REL1"),

0 commit comments

Comments
 (0)