-
Notifications
You must be signed in to change notification settings - Fork 133
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(credential-status-router): Added basic router implementation & verifier/ manager encapsulation #1022
base: feat/credentialStatusList2021-ESM
Are you sure you want to change the base?
feat(credential-status-router): Added basic router implementation & verifier/ manager encapsulation #1022
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { IAgentContext, IPluginMethodMap } from "./IAgent" | ||
import { CredentialStatusGenerateArgs, CredentialStatusRequestArgs, CredentialStatusUpdateArgs } from "./ICredentialStatusManager" | ||
import { ICheckCredentialStatusArgs } from "./ICredentialStatusVerifier" | ||
import { IResolver } from "./IResolver" | ||
import { CredentialStatus, CredentialStatusReference, VerifiableCredential } from "./vc-data-model" | ||
|
||
|
||
export interface ICredentialStatusRouter extends IPluginMethodMap { | ||
/** | ||
* Returns a list of available credential status methods | ||
*/ | ||
statusRouterGetStatusMethods(): Promise<string[]> | ||
|
||
/** | ||
* Returns the revocation status of a credential from a given managed method | ||
*/ | ||
statusRouterCheckStatus(args: ICheckCredentialStatusArgs, context: IAgentContext<IResolver>): Promise<CredentialStatus> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method should be described by the If the |
||
|
||
/** | ||
* Generates a `credentialStatus` property for a future credential, not yet signed. | ||
* | ||
* @param args - Input arguments for generating a `credentialStatus` property for a {@link VerifiableCredential} | ||
* @returns A {@link CredentialStatusReference} object | ||
*/ | ||
statusRouterGenerateStatus(args: CredentialStatusGenerateArgs): Promise<CredentialStatusReference> | ||
|
||
/** | ||
* Reads a credential with a `credentialStatus` property and returns the parsed credential. | ||
* | ||
* @param args - Input arguments to request the verifiable credential status value | ||
* @returns A {@link VerifiableCredential} object | ||
*/ | ||
statusRouterParseStatus(args: CredentialStatusRequestArgs, context: IAgentContext<any>): Promise<VerifiableCredential> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't really understand the use of this method. Can you help me figure out how it fits into the greater model? |
||
|
||
/** | ||
* Changes the status of an existing {@link VerifiableCredential}. | ||
* Commonly used to revoke an existing credential. | ||
* | ||
* @param args - Input arguments for updating the status(revoking) a credential | ||
*/ | ||
statusRouterUpdateStatus(args: CredentialStatusUpdateArgs): Promise<any> | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ | ||
module.exports = { | ||
preset: 'ts-jest', | ||
testEnvironment: 'node', | ||
modulePathIgnorePatterns: [""], | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { createAgent, CredentialStatus, VerifiableCredential } from "@veramo/core" | ||
import { DIDDocument } from "did-resolver" | ||
import { AbstractStatusMethod } from '../abstract-status-method' | ||
import { CredentialStatusRouter } from '../status-router' | ||
import { CredentialStatusReference } from '@veramo/core/src/types/vc-data-model' | ||
import { ICredentialStatusRouter } from '@veramo/core/src/types/ICredentialStatusRouter' | ||
import { ICredentialIssuer } from "@veramo/credential-w3c" | ||
|
||
const referenceDidDoc: DIDDocument = { id: 'did:example:1234' } | ||
const referenceCredentialStatus = <CredentialStatusReference>{ | ||
type: 'ExoticStatusMethod2022', | ||
id: 'some-exotic-id', | ||
} | ||
const referenceCredential: VerifiableCredential & { credentialStatus: CredentialStatusReference } = { | ||
'@context': [], | ||
issuanceDate: new Date().toISOString(), | ||
proof: {}, | ||
issuer: referenceDidDoc.id, | ||
credentialSubject: {}, | ||
credentialStatus: referenceCredentialStatus | ||
} | ||
|
||
class ExoticStatusMethod2022 extends AbstractStatusMethod { | ||
async checkCredentialStatus(args: any, context: any): Promise<CredentialStatus> { | ||
return { verified: true } | ||
} | ||
async credentialStatusRead(args: any, context: any): Promise<VerifiableCredential> { | ||
return referenceCredential | ||
} | ||
async credentialStatusGenerate(args: any): Promise<CredentialStatusReference> { | ||
return <CredentialStatusReference>referenceCredential.credentialStatus | ||
} | ||
async credentialStatusUpdate(args: any): Promise<any> { | ||
throw new Error("Method not implemented.") | ||
} | ||
} | ||
|
||
describe('@veramo/credential-status/status-router', () => { | ||
it('should route to the correct status method instance', async () => { | ||
const statusMethod = new ExoticStatusMethod2022() | ||
const agent = createAgent<ICredentialIssuer | ICredentialStatusRouter>({ | ||
plugins: [ | ||
new CredentialStatusRouter({ | ||
statusMethods: { | ||
ExoticStatusMethod2022: statusMethod, | ||
}, | ||
defaultStatusMethod: '', | ||
}) | ||
] | ||
}) | ||
|
||
const result = await agent.statusRouterCheckStatus({ | ||
credential: referenceCredential, | ||
}) | ||
|
||
expect(result).toStrictEqual({ verified: true }) | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { | ||
CredentialStatus, | ||
CredentialStatusGenerateArgs, | ||
CredentialStatusReference, | ||
CredentialStatusRequestArgs, | ||
CredentialStatusUpdateArgs, | ||
IAgentContext, | ||
ICheckCredentialStatusArgs, | ||
IResolver, | ||
VerifiableCredential | ||
} from "@veramo/core"; | ||
|
||
import { ICredentialIssuer } from '@veramo/credential-w3c' | ||
|
||
|
||
/** | ||
* An abstract class for the {@link @veramo/credential-status#CredentialStatusRouter} status method | ||
*/ | ||
export abstract class AbstractStatusMethod { | ||
abstract checkCredentialStatus( | ||
args: ICheckCredentialStatusArgs, | ||
context: IAgentContext<IResolver> | ||
): Promise<CredentialStatus> | ||
|
||
abstract credentialStatusRead( | ||
args: CredentialStatusRequestArgs, | ||
context: IAgentContext<ICredentialIssuer> | ||
): Promise<VerifiableCredential> | ||
|
||
abstract credentialStatusGenerate( | ||
args: CredentialStatusGenerateArgs | ||
): Promise<CredentialStatusReference> | ||
|
||
abstract credentialStatusUpdate( | ||
args: CredentialStatusUpdateArgs | ||
): Promise<any> | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export abstract class AbstractStatusStorage { | ||
abstract get(key: string): Promise<string | undefined> | ||
abstract set(key: string, value: string): Promise<void> | ||
abstract keys(): Promise<string[]> | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import { | ||
CredentialStatus, | ||
CredentialStatusGenerateArgs, | ||
CredentialStatusReference, | ||
CredentialStatusRequestArgs, | ||
CredentialStatusUpdateArgs, | ||
IAgentContext, | ||
IAgentPlugin, | ||
IAgentPluginSchema, | ||
ICheckCredentialStatusArgs, | ||
ICredentialStatusRouter, | ||
IResolver, | ||
VerifiableCredential, | ||
} from "@veramo/core"; | ||
import { ICredentialIssuer } from "@veramo/credential-w3c"; | ||
import { AbstractStatusMethod } from "./abstract-status-method"; | ||
import { AbstractStatusStorage } from "./abstract-status-storage"; | ||
|
||
/** | ||
* Agent plugin that implements {@link @veramo/core#ICredentialStatusRouter} interface | ||
* @public | ||
*/ | ||
export class CredentialStatusRouter implements IAgentPlugin { | ||
/** | ||
* Plugin methods | ||
* @public | ||
*/ | ||
readonly methods: ICredentialStatusRouter | ||
readonly schema?: IAgentPluginSchema | undefined | ||
|
||
private statusMethods: Record<string, AbstractStatusMethod> | ||
// Beta: Default status method that bypasses the router | ||
private defaultStatusMethod: string | ||
// Beta: Instantiate a default storage method | ||
private storage?: AbstractStatusStorage | ||
|
||
constructor(options: { | ||
statusMethods: Record<string, AbstractStatusMethod> | ||
defaultStatusMethod: string | ||
storage?: AbstractStatusStorage | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like the storage parameter is not actually used by this class. It looks safe to remove. |
||
}) { | ||
this.statusMethods = options.statusMethods | ||
this.defaultStatusMethod = options.defaultStatusMethod | ||
this.storage = options.storage | ||
this.methods = { | ||
statusRouterGetStatusMethods: this.statusRouterGetStatusMethods.bind(this), | ||
statusRouterCheckStatus: this.statusRouterCheckCredentialStatus.bind(this), | ||
statusRouterGenerateStatus: this.statusRouterGenerateStatus.bind(this), | ||
statusRouterParseStatus: this.statusRouterParseStatus.bind(this), | ||
statusRouterUpdateStatus: this.statusRouterUpdateStatus.bind(this), | ||
} | ||
} | ||
|
||
private getStatusMethod(statusReference: CredentialStatusReference): AbstractStatusMethod { | ||
let statusMethod: AbstractStatusMethod | undefined = this.statusMethods[statusReference.type] | ||
if (!statusMethod) { | ||
throw new Error(`invalid_argument: unrecognized method ${statusReference.type}`) | ||
} | ||
return statusMethod | ||
} | ||
|
||
/** {@inheritDoc @veramo/core#ICredentialStatusRouter.statusRouterGetStatusMethods} */ | ||
async statusRouterGetStatusMethods(): Promise<string[]> { | ||
return Object.keys(this.statusMethods) | ||
} | ||
|
||
/** {@inheritDoc @veramo/core#ICredentialStatusRouter.statusRouterCheckCredentialStatus} */ | ||
async statusRouterCheckCredentialStatus(args: ICheckCredentialStatusArgs, context: IAgentContext<IResolver>): Promise<CredentialStatus> { | ||
const statusMethod = this.getStatusMethod(args.credential?.credentialStatus!) | ||
return statusMethod.checkCredentialStatus(args, context) | ||
} | ||
|
||
/** {@inheritDoc @veramo/core#ICredentialStatusRouter.statusRouterGenerateStatus} */ | ||
async statusRouterGenerateStatus(args: CredentialStatusGenerateArgs): Promise<CredentialStatusReference> { | ||
const statusMethod = this.getStatusMethod({ id: '', type: args.type } as CredentialStatusReference) | ||
return statusMethod.credentialStatusGenerate(args) | ||
} | ||
|
||
/** {@inheritDoc @veramo/core#ICredentialStatusRouter.statusRouterParseStatus} */ | ||
async statusRouterParseStatus(args: CredentialStatusRequestArgs, context: IAgentContext<ICredentialIssuer>): Promise<VerifiableCredential> { | ||
const statusMethod = this.getStatusMethod(args.credential.credentialStatus) | ||
return statusMethod.credentialStatusRead(args, context) | ||
} | ||
|
||
/** {@inheritDoc @veramo/core#ICredentialStatusRouter.statusRouterUpdateStatus} */ | ||
async statusRouterUpdateStatus(args: CredentialStatusUpdateArgs): Promise<any> { | ||
const statusMethod = this.getStatusMethod(args.vc.credentialStatus!) | ||
return statusMethod.credentialStatusUpdate(args) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the purpose of this method?
I think that statusRead/statusCheck operations should be reserved for the
ICredentialStatusVerifier
interface, but perhaps I'm misunderstanding how this fits in.Can you add some description to it?