Skip to content

Commit

Permalink
test: fixes revocation test (#329)
Browse files Browse the repository at this point in the history
Signed-off-by: Allain Magyar <[email protected]>
  • Loading branch information
amagyar-iohk authored Nov 11, 2024
1 parent 8b0a7be commit 9f6f5dd
Show file tree
Hide file tree
Showing 9 changed files with 521 additions and 1,851 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@sdjwt @credential @sdkverificationsd
@sdjwt @credential @sdkverification
Feature: Verify SD+JWT presentation
The Edge Agent should be able to receive a verifiable credential from Cloud Agent and then send a presentation to another edge agent who will verify it

Expand Down
6 changes: 6 additions & 0 deletions integration-tests/e2e-tests/src/Utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { appendFile, writeFileSync } from "fs"
import crypto from "crypto"
import { Buffer } from 'buffer';

export class Utils {
static async asyncFilter<T>(arr: T[], predicate: (value: T, index: number, array: T[]) => Promise<boolean>) {
Expand Down Expand Up @@ -53,4 +54,9 @@ export class Utils {
}
return result
}

static decodeBase64URL(encodedString: string): string {
const buffer = Buffer.from(encodedString, "base64url")
return buffer.toString("utf8");
}
}
61 changes: 27 additions & 34 deletions integration-tests/e2e-tests/src/abilities/WalletSdk.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Ability, Discardable, Initialisable, Interaction, Question, QuestionAdapter } from "@serenity-js/core"
import type SDK from "@hyperledger/identus-edge-agent-sdk"
import axios from "axios"
import { CloudAgentConfiguration } from "../configuration/CloudAgentConfiguration"
import { axiosInstance, CloudAgentConfiguration } from "../configuration/CloudAgentConfiguration"
import InMemoryStore from "../configuration/inmemory"
import { randomUUID, UUID } from "crypto"

let instance: typeof import("@hyperledger/identus-edge-agent-sdk").default;
let instance: typeof import("@hyperledger/identus-edge-agent-sdk").default
// fallback in any case of dangling sdk agents
export const agentList: Map<string, WalletSdk> = new Map()

Expand All @@ -15,34 +15,27 @@ class ShortFormDIDResolverSample implements SDK.Domain.DIDResolver {

async resolve(didString: string): Promise<SDK.Domain.DIDDocument> {
const { Domain } = await WalletSdk.loadSDK()
const url = `${CloudAgentConfiguration.agentUrl}dids/${didString}`;
const response = await fetch(url, {
"headers": {
"accept": "*/*",
"accept-language": "en",
"cache-control": "no-cache",
"pragma": "no-cache",
"sec-gpc": "1"
},
"method": "GET",
"mode": "cors",
"credentials": "omit"

const response = await axiosInstance.get(`dids/${didString}`, {
headers: {
Accept: "*/*"
}
})
if (!response.ok) {
throw new Error('Failed to fetch data');
if (response.status != 200) {
throw new Error("Failed to fetch data")
}
const data: any = await response.json();
const didDocument = data.didDocument;
const data = response.data
const didDocument = data.didDocument

const servicesProperty = new Domain.Services(
didDocument.service
)
const verificationMethodsProperty = new Domain.VerificationMethods(
didDocument.verificationMethod
)
const coreProperties: SDK.Domain.DIDDocumentCoreProperty[] = [];
const authenticate: SDK.Domain.Authentication[] = [];
const assertion: SDK.Domain.AssertionMethod[] = [];
const coreProperties: SDK.Domain.DIDDocumentCoreProperty[] = []
const authenticate: SDK.Domain.Authentication[] = []
const assertion: SDK.Domain.AssertionMethod[] = []

for (const verificationMethod of didDocument.verificationMethod) {
const isAssertion = didDocument.assertionMethod.find((method) => method === verificationMethod.id)
Expand All @@ -51,20 +44,20 @@ class ShortFormDIDResolverSample implements SDK.Domain.DIDResolver {
}
const isAuthentication = didDocument.authentication.find((method) => method === verificationMethod.id)
if (isAuthentication) {
authenticate.push(new Domain.Authentication([isAuthentication], [verificationMethod]));
authenticate.push(new Domain.Authentication([isAuthentication], [verificationMethod]))
}
}

coreProperties.push(...authenticate);
coreProperties.push(servicesProperty);
coreProperties.push(verificationMethodsProperty);
coreProperties.push(...authenticate)
coreProperties.push(servicesProperty)
coreProperties.push(verificationMethodsProperty)

const resolved = new Domain.DIDDocument(
Domain.DID.fromString(didString),
coreProperties
);
)

return resolved;
return resolved
}
}

Expand Down Expand Up @@ -120,9 +113,9 @@ export class WalletSdk extends Ability implements Initialisable, Discardable {
}

static execute(callback: (sdk: SDK.Agent, messages: {
credentialOfferStack: SDK.Domain.Message[];
issuedCredentialStack: SDK.Domain.Message[];
proofRequestStack: SDK.Domain.Message[];
credentialOfferStack: SDK.Domain.Message[]
issuedCredentialStack: SDK.Domain.Message[]
proofRequestStack: SDK.Domain.Message[]
revocationStack: SDK.Domain.Message[],
presentationMessagesStack: SDK.Domain.Message[]

Expand All @@ -139,7 +132,7 @@ export class WalletSdk extends Ability implements Initialisable, Discardable {
}

static async loadSDK() {
instance ??= require("@hyperledger/identus-edge-agent-sdk");
instance ??= require("@hyperledger/identus-edge-agent-sdk")
return instance
}

Expand All @@ -162,9 +155,9 @@ export class WalletSdk extends Ability implements Initialisable, Discardable {
Castor
} = await WalletSdk.loadSDK()

const resolvers = [ShortFormDIDResolverSample];
const resolvers = [ShortFormDIDResolverSample]
const apollo = new Apollo()
const castor = new Castor(apollo, resolvers);
const castor = new Castor(apollo, resolvers)

this.store = new Store({
name: [...Array(30)].map(() => Math.random().toString(36)[2]).join(""),
Expand Down Expand Up @@ -249,7 +242,7 @@ class MessageQueue {
}

async processMessages() {
const SDK = await WalletSdk.loadSDK();
const SDK = await WalletSdk.loadSDK()
this.processingId = setInterval(() => {
if (!this.isEmpty()) {
const message: SDK.Domain.Message = this.dequeue()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ export class CloudAgentConfiguration {
await axiosInstance.get(
`did-registrar/dids/${this.publishedDid}`
)
return
} catch (err) {
Utils.appendToNotes("DID not found. Creating a new one and publishing it.")
this.publishedDid = await this.preparePublishedDid(Curve.Secp256k1)
Expand All @@ -52,7 +51,6 @@ export class CloudAgentConfiguration {
await axiosInstance.get(
`did-registrar/dids/${this.publishedEd25519Did}`
)
return
} catch (err) {
Utils.appendToNotes("Ed25519 DID not found. Creating a new one and publishing it.")
this.publishedEd25519Did = await this.preparePublishedDid(Curve.Ed25519)
Expand All @@ -65,7 +63,6 @@ export class CloudAgentConfiguration {
await axiosInstance.get(
`schema-registry/schemas/${this.jwtSchemaGuid}`
)
return
} catch (err) {
Utils.appendToNotes("JWT Schema not found. Creating a new one.")
this.jwtSchemaGuid = await this.prepareJwtSchema(this.publishedDid)
Expand All @@ -77,7 +74,6 @@ export class CloudAgentConfiguration {
await axiosInstance.get(
`schema-registry/schemas/${this.sdJWTSchemaGuid}`
)
return
} catch (err) {
Utils.appendToNotes("SDJWT Schema not found. Creating a new one.")
this.sdJWTSchemaGuid = await this.prepareJwtSchema(this.publishedEd25519Did)
Expand Down Expand Up @@ -111,24 +107,30 @@ export class CloudAgentConfiguration {

const assertionKey = new ManagedDIDKeyTemplate()
assertionKey.id = "key-assertion-1"
assertionKey.purpose = Purpose.AssertionMethod;
assertionKey.curve = curve;
assertionKey.purpose = Purpose.AssertionMethod
assertionKey.curve = curve

const authenticationKey = new ManagedDIDKeyTemplate()
authenticationKey.id = "key-authentication-1"
authenticationKey.purpose = Purpose.Authentication;
assertionKey.curve = curve;
authenticationKey.purpose = Purpose.Authentication
assertionKey.curve = curve

creationData.documentTemplate.publicKeys = [
assertionKey,
authenticationKey
]
creationData.documentTemplate.services = []

const creationResponse = await axiosInstance.post(
"did-registrar/dids",
creationData
)
let creationResponse
try {
creationResponse = await axiosInstance.post(
"did-registrar/dids",
creationData
)
} catch (e) {
console.log(e)
}

const longFormDid = creationResponse.data.longFormDid
const publicationResponse = await axiosInstance.post(
`did-registrar/dids/${longFormDid}/publications`
Expand Down Expand Up @@ -257,6 +259,7 @@ export class CloudAgentConfiguration {

export const axiosInstance = axios.create({
baseURL: CloudAgentConfiguration.agentUrl,
insecureHTTPParser: true,
timeout: 60000,
headers: {
Accept: "application/json,application/xml",
Expand Down
12 changes: 11 additions & 1 deletion integration-tests/e2e-tests/src/screenplay/Expectations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Expectation } from "@serenity-js/core"
import _ from "lodash"

export class Expectations {
static propertyValueToBe = <T>(jsonPath: string, expected: string) => {
static propertyValueToBe<T>(jsonPath: string, expected: string) {
return Expectation.define(
"propertyValueEqualTo", `contain path property [${jsonPath}] value equal to`,
(actual: T, expected: string) => {
Expand All @@ -11,6 +11,16 @@ export class Expectations {
)(expected)
}

static propertyIsMetFor<T>(jsonPath: string, matcher: (property: string) => Promise<boolean>) {
return Expectation.define(
"propertyValueEqualTo", `contain path property [${jsonPath}] to`,
async (actual: T) => {
return await matcher(_.get(actual, jsonPath))
}
)()
}


static equalsTo = Expectation.define(
"isEqualTo", "is equal to",
(actual: number, expected: number) =>
Expand Down
9 changes: 4 additions & 5 deletions integration-tests/e2e-tests/src/steps/EdgeAgentSteps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ Then("{actor} is dismissed",

Then("{actor} will request {actor} to verify the anonymous credential",
async function (verifierEdgeAgent: Actor, holderEdgeAgent: Actor) {
const SDK = await EdgeAgentWorkflow.instance;
const SDK = await EdgeAgentWorkflow.instance
await EdgeAgentWorkflow.createPeerDids(holderEdgeAgent, 1)
const holderDID = await holderEdgeAgent.answer(Notepad.notes().get("lastPeerDID"))
const claims = {
Expand All @@ -247,7 +247,7 @@ Then("{actor} will request {actor} to verify the anonymous credential",

Then("{actor} will request {actor} to verify the JWT credential",
async function (verifierEdgeAgent: Actor, holderEdgeAgent: Actor) {
const SDK = await EdgeAgentWorkflow.instance;
const SDK = await EdgeAgentWorkflow.instance
await EdgeAgentWorkflow.createPeerDids(holderEdgeAgent, 1)
const holderDID = await holderEdgeAgent.answer(Notepad.notes().get("lastPeerDID"))
const claims = {
Expand All @@ -265,7 +265,7 @@ Then("{actor} will request {actor} to verify the JWT credential",

Then("{actor} will request {actor} to verify the SD+JWT credential",
async function (verifierEdgeAgent: Actor, holderEdgeAgent: Actor) {
const SDK = await EdgeAgentWorkflow.instance;
const SDK = await EdgeAgentWorkflow.instance
await EdgeAgentWorkflow.createPeerDids(holderEdgeAgent, 1)
const holderDID = await holderEdgeAgent.answer(Notepad.notes().get("lastPeerDID"))
const claims: any = {
Expand All @@ -282,7 +282,7 @@ Then("{actor} will request {actor} to verify the SD+JWT credential",

Then("{actor} will request {actor} to verify the SD+JWT credential with non-existing claims",
async function (verifierEdgeAgent: Actor, holderEdgeAgent: Actor) {
const SDK = await EdgeAgentWorkflow.instance;
const SDK = await EdgeAgentWorkflow.instance
await EdgeAgentWorkflow.createPeerDids(holderEdgeAgent, 1)
const holderDID = await holderEdgeAgent.answer(Notepad.notes().get("lastPeerDID"))
const claims: any = {
Expand All @@ -302,7 +302,6 @@ When("{actor} sends the verification proof", async (edgeAgent: Actor) => {
await EdgeAgentWorkflow.presentVerificationRequest(edgeAgent)
})


Then("{actor} should receive an exception when trying to use a wrong anoncred credential", async (edgeAgent: Actor) => {
await EdgeAgentWorkflow.waitForProofRequest(edgeAgent)
await EdgeAgentWorkflow.tryToPresentVerificationRequestWithWrongAnoncred(edgeAgent)
Expand Down
52 changes: 43 additions & 9 deletions integration-tests/e2e-tests/src/workflow/CloudAgentWorkflow.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Actor, Duration, Notepad, Wait } from "@serenity-js/core"
import { LastResponse, PatchRequest, PostRequest, Send } from "@serenity-js/rest"
import { GetRequest, LastResponse, PatchRequest, PostRequest, Send } from "@serenity-js/rest"
import { Ensure, equals } from "@serenity-js/assertions"
import { HttpStatusCode } from "axios"
import { Expectations } from "../screenplay/Expectations"
Expand All @@ -15,6 +15,7 @@ import {
import { CloudAgentConfiguration } from "../configuration/CloudAgentConfiguration"
import { Utils } from "../Utils"
import SDK from "@hyperledger/identus-edge-agent-sdk"
import { JWTRevocationStatus } from "@hyperledger/identus-edge-agent-sdk/build/domain"

export class CloudAgentWorkflow {
static async createConnection(cloudAgent: Actor, label?: string, goalCode?: string, goal?: string) {
Expand Down Expand Up @@ -94,17 +95,17 @@ export class CloudAgentWorkflow {
}

static async offerSDJWTCredential(cloudAgent: Actor) {
const credential = new CreateIssueCredentialRecordRequest();
credential.validityPeriod = 360000;
const credential = new CreateIssueCredentialRecordRequest()
credential.validityPeriod = 360000
credential.claims = {
"automationRequired": "required value",
};
credential.automaticIssuance = true;
credential.issuingDID = CloudAgentConfiguration.publishedEd25519Did;
}
credential.automaticIssuance = true
credential.issuingDID = CloudAgentConfiguration.publishedEd25519Did
credential.connectionId = await cloudAgent.answer<string>(
Notepad.notes().get("connectionId")
);
credential.credentialFormat = "SDJWT";
)
credential.credentialFormat = "SDJWT"
await cloudAgent.attemptsTo(
Send.a(PostRequest.to("issue-credentials/credential-offers").with(credential)),
Ensure.that(LastResponse.status(), equals(HttpStatusCode.Created)),
Expand Down Expand Up @@ -288,14 +289,47 @@ export class CloudAgentWorkflow {
)
}

static async getCredential(cloudAgent: Actor, recordId: string) {//}: Promise<Credential> {
await cloudAgent.attemptsTo(
Send.a(GetRequest.to(`issue-credentials/records/${recordId}`)),
Ensure.that(LastResponse.status(), equals(HttpStatusCode.Ok))
)
return await LastResponse.body().answeredBy(cloudAgent)
}

private static instance: typeof import("@hyperledger/identus-edge-agent-sdk").default

static async getCredentialStatusList(cloudAgent: Actor, recordIdList: string[]): Promise<Map<string, string>> {
CloudAgentWorkflow.instance ??= require("@hyperledger/identus-edge-agent-sdk")
const statusRegistry = new Map<string, string>()
for (const recordId of recordIdList) {
const credentialResponse = await this.getCredential(cloudAgent, recordId)
const jwtString = Utils.decodeBase64URL(credentialResponse.credential)
const decoded = CloudAgentWorkflow.instance.JWTCredential.fromJWS(jwtString)
const credentialStatus = decoded.vc.credentialStatus as JWTRevocationStatus
const statusList = credentialStatus.statusListCredential
statusRegistry.set(recordId, statusList)
}
return statusRegistry
}

static async revokeCredential(cloudAgent: Actor, numberOfRevokedCredentials: number) {
const revokedRecordIdList = []
const recordIdList = await cloudAgent.answer(Notepad.notes().get("recordIdList"))
const statusesList = await this.getCredentialStatusList(cloudAgent, recordIdList)
await Utils.repeat(numberOfRevokedCredentials, async () => {
const recordId = recordIdList.shift()!
await cloudAgent.attemptsTo(
Send.a(GetRequest.to(statusesList.get(recordId))),
Notepad.notes().set("statusListEncoded", LastResponse.body().credentialSubject.encodedList),
Send.a(PatchRequest.to(`credential-status/revoke-credential/${recordId}`)),
Ensure.that(LastResponse.status(), equals(HttpStatusCode.Ok))
Ensure.that(LastResponse.status(), equals(HttpStatusCode.Ok)),
Wait.upTo(Duration.ofSeconds(60)).until(
Questions.httpGet(statusesList.get(recordId)),
Expectations.propertyIsMetFor("credentialSubject.encodedList", async (property) => {
return property != await cloudAgent.answer(Notepad.notes().get("statusListEncoded"))
})
)
)
revokedRecordIdList.push(recordId)
})
Expand Down
Loading

1 comment on commit 9f6f5dd

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lines Statements Branches Functions
Coverage: 75%
76.08% (3633/4775) 66.49% (1665/2504) 80.29% (864/1076)

Please sign in to comment.