Skip to content

Commit

Permalink
Merge branch 'main' into simplify-IdentityDeletionProcess-handling-on…
Browse files Browse the repository at this point in the history
…-second-device-by-republishing-events
  • Loading branch information
mergify[bot] authored Dec 3, 2024
2 parents 0b43107 + 20398db commit 03ea3c3
Show file tree
Hide file tree
Showing 17 changed files with 1,073 additions and 224 deletions.
8 changes: 8 additions & 0 deletions packages/consumption/src/consumption/ConsumptionCoreErrors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,14 @@ class Requests {
return new CoreError("error.consumption.requests.missingRelationship", message);
}

public peerIsDeleted(message: string) {
return new CoreError("error.consumption.requests.peerIsDeleted", message);
}

public peerIsInDeletion(message: string) {
return new CoreError("error.consumption.requests.peerIsInDeletion", message);
}

public inheritedFromItem(message: string) {
return new ApplicationError("error.consumption.requests.validation.inheritedFromItem", message);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ServalError } from "@js-soft/ts-serval";
import { EventBus } from "@js-soft/ts-utils";
import { RequestItem, RequestItemGroup, Response, ResponseItemDerivations, ResponseItemGroup, ResponseResult } from "@nmshd/content";
import { CoreAddress, CoreDate, CoreId, ICoreAddress, ICoreId } from "@nmshd/core-types";
import { Message, Relationship, RelationshipStatus, RelationshipTemplate, SynchronizedCollection, TransportCoreErrors } from "@nmshd/transport";
import { Message, PeerDeletionStatus, Relationship, RelationshipStatus, RelationshipTemplate, SynchronizedCollection, TransportCoreErrors } from "@nmshd/transport";
import { ConsumptionBaseController } from "../../../consumption/ConsumptionBaseController";
import { ConsumptionController } from "../../../consumption/ConsumptionController";
import { ConsumptionControllerName } from "../../../consumption/ConsumptionControllerName";
Expand Down Expand Up @@ -193,6 +193,18 @@ export class IncomingRequestsController extends ConsumptionBaseController {

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

if (relationship?.peerDeletionInfo?.deletionStatus === PeerDeletionStatus.ToBeDeleted) {
return ValidationResult.error(
ConsumptionCoreErrors.requests.peerIsInDeletion(`You cannot decide a Request from peer '${request.peer.toString()}' since the peer is in deletion.`)
);
}

if (relationship?.peerDeletionInfo?.deletionStatus === PeerDeletionStatus.Deleted) {
return ValidationResult.error(
ConsumptionCoreErrors.requests.peerIsDeleted(`You cannot decide a Request from peer '${request.peer.toString()}' since the peer is deleted.`)
);
}

const validationResult = this.decideRequestParamsValidator.validate(params, request);
if (validationResult.isError()) return validationResult;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { EventBus } from "@js-soft/ts-utils";
import { DeleteAttributeRequestItem, RelationshipTemplateContent, Request, RequestItem, RequestItemGroup, Response, ResponseItem, ResponseItemGroup } from "@nmshd/content";
import { CoreAddress, CoreDate, CoreId, ICoreId } from "@nmshd/core-types";
import { Message, Relationship, RelationshipStatus, RelationshipTemplate, SynchronizedCollection, TransportCoreErrors } from "@nmshd/transport";
import { Message, PeerDeletionStatus, Relationship, RelationshipStatus, RelationshipTemplate, SynchronizedCollection, TransportCoreErrors } from "@nmshd/transport";
import { ConsumptionBaseController } from "../../../consumption/ConsumptionBaseController";
import { ConsumptionController } from "../../../consumption/ConsumptionController";
import { ConsumptionControllerName } from "../../../consumption/ConsumptionControllerName";
Expand Down Expand Up @@ -62,6 +62,18 @@ export class OutgoingRequestsController extends ConsumptionBaseController {
)
);
}

if (relationship.peerDeletionInfo?.deletionStatus === PeerDeletionStatus.ToBeDeleted) {
return ValidationResult.error(
ConsumptionCoreErrors.requests.peerIsInDeletion(`You cannot create a Request to peer '${parsedParams.peer.toString()}' since the peer is in deletion.`)
);
}

if (relationship.peerDeletionInfo?.deletionStatus === PeerDeletionStatus.Deleted) {
return ValidationResult.error(
ConsumptionCoreErrors.requests.peerIsDeleted(`You cannot create a Request to peer '${parsedParams.peer.toString()}' since the peer is deleted.`)
);
}
}

const innerResults = await this.canCreateItems(parsedParams.content, parsedParams.peer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,8 @@ describe("IncomingRequestsController", function () {
await Given.anIncomingRequestInStatus(LocalRequestStatus.DecisionRequired);
const validationResult = await When.iCallCanAccept();
expect(validationResult).errorValidationResult({
code: "error.consumption.requests.wrongRelationshipStatus"
code: "error.consumption.requests.wrongRelationshipStatus",
message: "You cannot decide a request from 'did:e:a-domain:dids:anidentity' since the relationship is in status 'Terminated'."
});
});

Expand All @@ -422,7 +423,28 @@ describe("IncomingRequestsController", function () {
await Given.anIncomingRequestInStatus(LocalRequestStatus.DecisionRequired);
const validationResult = await When.iCallCanAccept();
expect(validationResult).errorValidationResult({
code: "error.consumption.requests.wrongRelationshipStatus"
code: "error.consumption.requests.wrongRelationshipStatus",
message: "You cannot decide a request from 'did:e:a-domain:dids:anidentity' since the relationship is in status 'DeletionProposed'."
});
});

test("returns 'error' on relationship with peer which is in deletion", async function () {
await Given.aRelationshipToPeerInDeletion();
await Given.anIncomingRequestInStatus(LocalRequestStatus.DecisionRequired);
const validationResult = await When.iCallCanAccept();
expect(validationResult).errorValidationResult({
code: "error.consumption.requests.peerIsInDeletion",
message: "You cannot decide a Request from peer 'did:e:a-domain:dids:anidentity' since the peer is in deletion."
});
});

test("returns 'error' on relationship with peer which is deleted", async function () {
await Given.aRelationshipToDeletedPeer();
await Given.anIncomingRequestInStatus(LocalRequestStatus.DecisionRequired);
const validationResult = await When.iCallCanAccept();
expect(validationResult).errorValidationResult({
code: "error.consumption.requests.wrongRelationshipStatus",
message: "You cannot decide a request from 'did:e:a-domain:dids:anidentity' since the relationship is in status 'DeletionProposed'."
});
});
});
Expand Down Expand Up @@ -607,7 +629,8 @@ describe("IncomingRequestsController", function () {
await Given.anIncomingRequestInStatus(LocalRequestStatus.DecisionRequired);
const validationResult = await When.iCallCanReject();
expect(validationResult).errorValidationResult({
code: "error.consumption.requests.wrongRelationshipStatus"
code: "error.consumption.requests.wrongRelationshipStatus",
message: "You cannot decide a request from 'did:e:a-domain:dids:anidentity' since the relationship is in status 'Terminated'."
});
});

Expand All @@ -616,7 +639,28 @@ describe("IncomingRequestsController", function () {
await Given.anIncomingRequestInStatus(LocalRequestStatus.DecisionRequired);
const validationResult = await When.iCallCanReject();
expect(validationResult).errorValidationResult({
code: "error.consumption.requests.wrongRelationshipStatus"
code: "error.consumption.requests.wrongRelationshipStatus",
message: "You cannot decide a request from 'did:e:a-domain:dids:anidentity' since the relationship is in status 'DeletionProposed'."
});
});

test("returns 'error' on relationship with peer which is in deletion", async function () {
await Given.aRelationshipToPeerInDeletion();
await Given.anIncomingRequestInStatus(LocalRequestStatus.DecisionRequired);
const validationResult = await When.iCallCanReject();
expect(validationResult).errorValidationResult({
code: "error.consumption.requests.peerIsInDeletion",
message: "You cannot decide a Request from peer 'did:e:a-domain:dids:anidentity' since the peer is in deletion."
});
});

test("returns 'error' on relationship with peer which is deleted", async function () {
await Given.aRelationshipToDeletedPeer();
await Given.anIncomingRequestInStatus(LocalRequestStatus.DecisionRequired);
const validationResult = await When.iCallCanReject();
expect(validationResult).errorValidationResult({
code: "error.consumption.requests.wrongRelationshipStatus",
message: "You cannot decide a request from 'did:e:a-domain:dids:anidentity' since the relationship is in status 'DeletionProposed'."
});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,67 @@ describe("OutgoingRequestsController", function () {
});
});

describe("CanCreate (on terminated relationship)", function () {
test("returns a validation result that contains an error if the relationship is terminated", async function () {
await Given.aTerminatedRelationshipToIdentity();
const validationResult = await When.iCallCanCreateForAnOutgoingRequest({
content: {
items: [
TestRequestItem.from({
mustBeAccepted: false,
shouldFailAtCanCreateOutgoingRequestItem: true
})
]
}
});

expect(validationResult).errorValidationResult({
code: "error.consumption.requests.wrongRelationshipStatus",
message: "You cannot create a request to 'did:e:a-domain:dids:anidentity' since the relationship is in status 'Terminated'"
});
});
});

describe("CanCreate (with peer in deletion or deleted peer)", function () {
test("returns a validation result that contains an error for requests to a peer which is in deletion", async function () {
await Given.aRelationshipToPeerInDeletion();
const validationResult = await When.iCallCanCreateForAnOutgoingRequest({
content: {
items: [
TestRequestItem.from({
mustBeAccepted: false,
shouldFailAtCanCreateOutgoingRequestItem: true
})
]
}
});

expect(validationResult).errorValidationResult({
code: "error.consumption.requests.peerIsInDeletion",
message: "You cannot create a Request to peer 'did:e:a-domain:dids:anidentity' since the peer is in deletion."
});
});

test("returns a validation result that contains an error for requests to a peer which is deleted", async function () {
await Given.aRelationshipToDeletedPeer();
const validationResult = await When.iCallCanCreateForAnOutgoingRequest({
content: {
items: [
TestRequestItem.from({
mustBeAccepted: false,
shouldFailAtCanCreateOutgoingRequestItem: true
})
]
}
});

expect(validationResult).errorValidationResult({
code: "error.consumption.requests.wrongRelationshipStatus",
message: "You cannot create a request to 'did:e:a-domain:dids:anidentity' since the relationship is in status 'DeletionProposed'."
});
});
});

describe("Create (on active relationship)", function () {
beforeEach(async function () {
await Given.anActiveRelationshipToIdentity();
Expand Down Expand Up @@ -804,23 +865,4 @@ describe("OutgoingRequestsController", function () {
await Then.itThrowsAnErrorWithTheErrorMessage("*Local Request has to be in status 'Draft'*");
});
});

describe("CanCreate (on terminated relationship)", function () {
test("returns 'error' when the relationship is terminated", async function () {
await Given.aTerminatedRelationshipToIdentity();
const validationResult = await When.iCallCanCreateForAnOutgoingRequest({
content: {
items: [
TestRequestItem.from({
mustBeAccepted: false,
shouldFailAtCanCreateOutgoingRequestItem: true
})
]
}
});
expect(validationResult).errorValidationResult({
code: "error.consumption.requests.wrongRelationshipStatus"
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,18 @@ export class RequestsGiven {
return Promise.resolve();
}

public aRelationshipToPeerInDeletion(): Promise<void> {
this.context.relationshipToReturnFromGetRelationshipToIdentity = TestObjectFactory.createRelationshipToPeerInDeletion();

return Promise.resolve();
}

public aRelationshipToDeletedPeer(): Promise<void> {
this.context.relationshipToReturnFromGetRelationshipToIdentity = TestObjectFactory.createRelationshipToDeletedPeer();

return Promise.resolve();
}

public aDeletionProposedRelationshipToIdentity(): Promise<void> {
this.context.relationshipToReturnFromGetRelationshipToIdentity = TestObjectFactory.createDeletionProposedRelationship();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import {
IRelationship,
IRelationshipTemplate,
Message,
PeerDeletionInfo,
PeerDeletionStatus,
Relationship,
RelationshipAuditLogEntryReason,
RelationshipStatus,
Expand Down Expand Up @@ -164,6 +166,112 @@ export class TestObjectFactory {
});
}

public static createRelationshipToPeerInDeletion(properties?: Partial<IRelationship>): Relationship {
return Relationship.from({
id: properties?.id ?? CoreId.from("REL1"),
peer:
properties?.peer ??
Identity.from({
address: CoreAddress.from("did:e:a-domain:dids:anidentity"),
publicKey: CryptoSignaturePublicKey.from({
algorithm: CryptoSignatureAlgorithm.ECDSA_ED25519,
publicKey: CoreBuffer.from("L1sPFQgS5CxgGs1ejBcWCQLCpeFXbRc1TQnSpuHQqDQ")
})
}),
peerDeletionInfo:
properties?.peerDeletionInfo ??
PeerDeletionInfo.from({
deletionStatus: PeerDeletionStatus.ToBeDeleted,
deletionDate: CoreDate.from("2100-01-03T00:00:00.000Z")
}),
status: properties?.status ?? RelationshipStatus.Active,
relationshipSecretId: properties?.relationshipSecretId ?? CoreId.from("RELSEC1"),
cachedAt: properties?.cachedAt ?? CoreDate.from("2020-01-03T00:00:00.000Z"),
cache:
properties?.cache ??
CachedRelationship.from({
creationContent: {},
auditLog: [
{
createdAt: CoreDate.from("2020-01-01T00:00:00.000Z"),
createdBy: CoreAddress.from("did:e:a-domain:dids:anidentity2"),
createdByDevice: CoreId.from("DVC1"),
reason: RelationshipAuditLogEntryReason.Creation,
newStatus: RelationshipStatus.Pending
},

{
createdAt: CoreDate.from("2020-01-02T00:00:00.000Z"),
createdBy: CoreAddress.from("did:e:a-domain:dids:anidentity"),
createdByDevice: CoreId.from("DVC1"),
reason: RelationshipAuditLogEntryReason.AcceptanceOfCreation,
oldStatus: RelationshipStatus.Pending,
newStatus: RelationshipStatus.Active
}
],
template: this.createIncomingRelationshipTemplate()
})
});
}

public static createRelationshipToDeletedPeer(properties?: Partial<IRelationship>): Relationship {
return Relationship.from({
id: properties?.id ?? CoreId.from("REL1"),
peer:
properties?.peer ??
Identity.from({
address: CoreAddress.from("did:e:a-domain:dids:anidentity"),
publicKey: CryptoSignaturePublicKey.from({
algorithm: CryptoSignatureAlgorithm.ECDSA_ED25519,
publicKey: CoreBuffer.from("L1sPFQgS5CxgGs1ejBcWCQLCpeFXbRc1TQnSpuHQqDQ")
})
}),
peerDeletionInfo:
properties?.peerDeletionInfo ??
PeerDeletionInfo.from({
deletionStatus: PeerDeletionStatus.Deleted,
deletionDate: CoreDate.from("2022-01-03T00:00:00.000Z")
}),
status: properties?.status ?? RelationshipStatus.DeletionProposed,
relationshipSecretId: properties?.relationshipSecretId ?? CoreId.from("RELSEC1"),
cachedAt: properties?.cachedAt ?? CoreDate.from("2022-01-03T00:00:00.000Z"),
cache:
properties?.cache ??
CachedRelationship.from({
creationContent: {},
auditLog: [
{
createdAt: CoreDate.from("2020-01-01T00:00:00.000Z"),
createdBy: CoreAddress.from("did:e:a-domain:dids:anidentity2"),
createdByDevice: CoreId.from("DVC1"),
reason: RelationshipAuditLogEntryReason.Creation,
newStatus: RelationshipStatus.Pending
},

{
createdAt: CoreDate.from("2020-01-02T00:00:00.000Z"),
createdBy: CoreAddress.from("did:e:a-domain:dids:anidentity"),
createdByDevice: CoreId.from("DVC1"),
reason: RelationshipAuditLogEntryReason.AcceptanceOfCreation,
oldStatus: RelationshipStatus.Pending,
newStatus: RelationshipStatus.Active
},

{
createdAt: CoreDate.from("2022-01-03T00:00:00.000Z"),
createdBy: CoreAddress.from("did:e:a-domain:dids:anidentity"),
createdByDevice: CoreId.from("DVC1"),
// must be DecompositionDueToIdentityDeletion in the future
reason: RelationshipAuditLogEntryReason.Decomposition,
oldStatus: RelationshipStatus.Active,
newStatus: RelationshipStatus.DeletionProposed
}
],
template: this.createIncomingRelationshipTemplate()
})
});
}

public static createDeletionProposedRelationship(properties?: Partial<IRelationship>): Relationship {
return Relationship.from({
id: properties?.id ?? CoreId.from("REL1"),
Expand Down
Loading

0 comments on commit 03ea3c3

Please sign in to comment.