Skip to content

Commit

Permalink
feat: Add resource tracking [DEV-3395] (#423)
Browse files Browse the repository at this point in the history
* Fix for publish option

* - Add resource tracking
- Fix credential actions for encrypted statusList

* - Small refactoring
- Add resource tracking for credential operations

* Format edits

* Error text changing
  • Loading branch information
Andrew Nikitin committed Nov 8, 2023
1 parent 17da0f4 commit 9b49b40
Show file tree
Hide file tree
Showing 18 changed files with 765 additions and 139 deletions.
110 changes: 60 additions & 50 deletions package-lock.json

Large diffs are not rendered by default.

180 changes: 150 additions & 30 deletions src/controllers/credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import { check, query, validationResult } from 'express-validator';
import { Credentials } from '../services/credentials.js';
import { IdentityServiceStrategySetup } from '../services/identity/index.js';
import jwt_decode from 'jwt-decode';
import type { ITrackOperation } from '../types/shared.js';
import { Cheqd } from '@cheqd/did-provider-cheqd';
import { OPERATION_CATEGORY_NAME_CREDENTIAL } from '../types/constants.js';

export class CredentialController {
public static issueValidator = [
Expand Down Expand Up @@ -245,7 +248,7 @@ export class CredentialController {
if (result.error) {
return response.status(StatusCodes.BAD_REQUEST).json({
verified: result.verified,
error: result.error,
error: result.error.message,
});
}
return response.status(StatusCodes.OK).json(result);
Expand Down Expand Up @@ -300,18 +303,55 @@ export class CredentialController {
if (!result.isEmpty()) {
return response.status(StatusCodes.BAD_REQUEST).json({ error: result.array()[0].msg });
}

const publish = request.query.publish === 'false' ? false : true;
try {
return response
.status(StatusCodes.OK)
.json(
await new IdentityServiceStrategySetup(response.locals.customer.customerId).agent.revokeCredentials(
request.body.credential,
publish,
response.locals.customer
)
);
const publish = request.query.publish === 'false' ? false : true;
// Get symmetric key
const symmetricKey = request.body.symmetricKey as string;
// Get strategy e.g. psotgres or local
const identityServiceStrategySetup = new IdentityServiceStrategySetup(response.locals.customer.customerId);
const result = await identityServiceStrategySetup.agent.revokeCredentials(
request.body.credential,
publish,
response.locals.customer,
symmetricKey
);
// Track operation if revocation was successful and publish is true
// Otherwise the StatusList2021 publisher should manually publish the resource
// and it will be tracked there
if (!result.error && result.resourceMetadata && publish) {
// decode credential for getting issuer did
const credential =
typeof request.body.credential === 'string'
? await Cheqd.decodeCredentialJWT(request.body.credential)
: request.body.credential;
// get issuer did
const issuerDid =
typeof credential.issuer === 'string'
? credential.issuer
: (credential.issuer as { id: string }).id;
const trackInfo = {
category: OPERATION_CATEGORY_NAME_CREDENTIAL,
operation: 'revoke',
customer: response.locals.customer,
user: response.locals.user,
did: issuerDid,
data: {
encrypted: result.statusList?.metadata?.encrypted,
resource: result.resourceMetadata,
symmetricKey: '',
},
} as ITrackOperation;

// Track operation
const trackResult = await identityServiceStrategySetup.agent.trackOperation(trackInfo);
if (trackResult.error) {
return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
error: trackResult.error,
});
}
}
// Return Ok response
return response.status(StatusCodes.OK).json(result);
} catch (error) {
return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
error: `${error}`,
Expand Down Expand Up @@ -363,13 +403,56 @@ export class CredentialController {
}

try {
return response
.status(StatusCodes.OK)
.json(
await new IdentityServiceStrategySetup(
response.locals.customer.customerId
).agent.suspendCredentials(request.body.credential, request.body.publish, response.locals.customer)
);
const publish = request.query.publish === 'false' ? false : true;
// Get symmetric key
const symmetricKey = request.body.symmetricKey as string;
// Get strategy e.g. psotgres or local
const identityServiceStrategySetup = new IdentityServiceStrategySetup(response.locals.customer.customerId);

const result = await identityServiceStrategySetup.agent.suspendCredentials(
request.body.credential,
publish,
response.locals.customer,
symmetricKey
);

// Track operation if suspension was successful and publish is true
// Otherwise the StatusList2021 publisher should manually publish the resource
// and it will be tracked there
if (!result.error && result.resourceMetadata && publish) {
// decode credential for getting issuer did
const credential =
typeof request.body.credential === 'string'
? await Cheqd.decodeCredentialJWT(request.body.credential)
: request.body.credential;
// get issuer did
const issuerDid =
typeof credential.issuer === 'string'
? credential.issuer
: (credential.issuer as { id: string }).id;
const trackInfo = {
category: OPERATION_CATEGORY_NAME_CREDENTIAL,
operation: 'suspend',
customer: response.locals.customer,
user: response.locals.user,
did: issuerDid,
data: {
encrypted: result.statusList?.metadata?.encrypted,
resource: result.resourceMetadata,
symmetricKey: '',
},
} as ITrackOperation;

// Track operation
const trackResult = await identityServiceStrategySetup.agent.trackOperation(trackInfo);
if (trackResult.error) {
return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
error: trackResult.error,
});
}
}

return response.status(StatusCodes.OK).json(result);
} catch (error) {
return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
error: `${error}`,
Expand Down Expand Up @@ -421,17 +504,54 @@ export class CredentialController {
}

try {
return response
.status(StatusCodes.OK)
.json(
await new IdentityServiceStrategySetup(
response.locals.customer.customerId
).agent.reinstateCredentials(
request.body.credential,
request.body.publish,
response.locals.customer
)
);
const publish = request.query.publish === 'false' ? false : true;
// Get symmetric key
const symmetricKey = request.body.symmetricKey as string;
// Get strategy e.g. psotgres or local
const identityServiceStrategySetup = new IdentityServiceStrategySetup(response.locals.customer.customerId);
const result = await identityServiceStrategySetup.agent.reinstateCredentials(
request.body.credential,
publish,
response.locals.customer,
symmetricKey
);
// Track operation if the process of reinstantiating was successful and publish is true
// Otherwise the StatusList2021 publisher should manually publish the resource
// and it will be tracked there
if (!result.error && result.resourceMetadata && publish) {
// decode credential for getting issuer did
const credential =
typeof request.body.credential === 'string'
? await Cheqd.decodeCredentialJWT(request.body.credential)
: request.body.credential;
// get issuer did
const issuerDid =
typeof credential.issuer === 'string'
? credential.issuer
: (credential.issuer as { id: string }).id;
const trackInfo = {
category: OPERATION_CATEGORY_NAME_CREDENTIAL,
operation: 'reinstate',
customer: response.locals.customer,
user: response.locals.user,
did: issuerDid,
data: {
encrypted: result.statusList?.metadata?.encrypted,
resource: result.resourceMetadata,
symmetricKey: '',
},
} as ITrackOperation;

// Track operation
const trackResult = await identityServiceStrategySetup.agent.trackOperation(trackInfo);
if (trackResult.error) {
return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
error: trackResult.error,
});
}
}
// Return Ok response
return response.status(StatusCodes.OK).json(result);
} catch (error) {
return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
error: `${error}`,
Expand Down
40 changes: 32 additions & 8 deletions src/controllers/issuer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import {
import { DIDMetadataDereferencingResult, DefaultResolverUrl } from '@cheqd/did-provider-cheqd';
import { bases } from 'multiformats/basics';
import { base64ToBytes } from 'did-jwt';
import type { CreateDidRequestBody } from '../types/shared.js';
import type { CreateDidRequestBody, ITrackOperation } from '../types/shared.js';
import { OPERATION_CATEGORY_NAME_RESOURCE } from '../types/constants.js';

export class IssuerController {
// ToDo: improve validation in a "bail" fashion
Expand Down Expand Up @@ -543,13 +544,12 @@ export class IssuerController {

const { did } = request.params;
const { data, encoding, name, type, alsoKnownAs, version, network } = request.body;
const identityServiceStrategySetup = new IdentityServiceStrategySetup(response.locals.customer.customerId);

let resourcePayload: Partial<MsgCreateResourcePayload> = {};
try {
// check if did is registered on the ledger
const { didDocument, didDocumentMetadata } = await new IdentityServiceStrategySetup(
response.locals.customer.customerId
).agent.resolveDid(did);
const { didDocument, didDocumentMetadata } = await identityServiceStrategySetup.agent.resolveDid(did);
if (!didDocument || !didDocumentMetadata || didDocumentMetadata.deactivated) {
return response.status(StatusCodes.BAD_REQUEST).send({
error: `${did} is a either Deactivated or Not found`,
Expand All @@ -565,18 +565,42 @@ export class IssuerController {
version,
alsoKnownAs,
};
const result = await new IdentityServiceStrategySetup(
response.locals.customer.customerId
).agent.createResource(network || did.split(':')[2], resourcePayload, response.locals.customer);
const result = await identityServiceStrategySetup.agent.createResource(
network || did.split(':')[2],
resourcePayload,
response.locals.customer
);

if (result) {
const url = new URL(
`${process.env.RESOLVER_URL || DefaultResolverUrl}${did}?` +
`resourceId=${resourcePayload.id}&resourceMetadata=true`
);
const didDereferencing = (await (await fetch(url)).json()) as DIDMetadataDereferencingResult;
const resource = didDereferencing.contentStream.linkedResourceMetadata[0];

// track resource creation
const trackResourceInfo = {
category: OPERATION_CATEGORY_NAME_RESOURCE,
operation: 'createResource',
customer: response.locals.customer,
did,
data: {
resource: resource,
encrypted: false,
symmetricKey: '',
},
} as ITrackOperation;

const trackResult = await identityServiceStrategySetup.agent.trackOperation(trackResourceInfo);
if (trackResult.error) {
return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
error: `${trackResult.error}`,
});
}

return response.status(StatusCodes.CREATED).json({
resource: didDereferencing.contentStream.linkedResourceMetadata[0],
resource,
});
} else {
return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
Expand Down
Loading

0 comments on commit 9b49b40

Please sign in to comment.