From 623f8ad4dd41858df4916e23170f4a1c973292f5 Mon Sep 17 00:00:00 2001 From: DaevMithran Date: Thu, 29 Jun 2023 16:45:56 +0530 Subject: [PATCH] feat: Refactor inputs --- src/controllers/issuer.ts | 92 +++++++---- src/controllers/revocation.ts | 8 +- src/static/swagger.json | 293 ++++++++++++++++++++++------------ 3 files changed, 256 insertions(+), 137 deletions(-) diff --git a/src/controllers/issuer.ts b/src/controllers/issuer.ts index 05aafa46..8fb92a6a 100644 --- a/src/controllers/issuer.ts +++ b/src/controllers/issuer.ts @@ -16,17 +16,13 @@ export class IssuerController { const { valid } = validateSpecCompliantPayload(value) return valid }).withMessage('Invalid didDocument'), - check('secret.verificationMethod.type') + check('verificationMethodType') .optional() .isString() .isIn([VerificationMethods.Ed255192020, VerificationMethods.Ed255192018, VerificationMethods.JWK]) - .withMessage('Invalid verificationMethod'), - check('secret.verificationMethod.id') - .optional() - .isString() - .withMessage('Invalid verificationMethod'), - check('options.methodSpecificIdAlgo').optional().isString().isIn([MethodSpecificIdAlgo.Base58, MethodSpecificIdAlgo.Uuid]).withMessage('Invalid methodSpecificIdAlgo'), - check('options.network').optional().isString().isIn([CheqdNetwork.Mainnet, CheqdNetwork.Testnet]).withMessage('Invalid network'), + .withMessage('Invalid verificationMethod'), + check('methodSpecificIdAlgo').optional().isString().isIn([MethodSpecificIdAlgo.Base58, MethodSpecificIdAlgo.Uuid]).withMessage('Invalid methodSpecificIdAlgo'), + check('network').optional().isString().isIn([CheqdNetwork.Mainnet, CheqdNetwork.Testnet]).withMessage('Invalid network'), ] public static updateValidator = [ @@ -42,13 +38,11 @@ export class IssuerController { public static resourceValidator = [ param('did').exists().isString().contains('did:cheqd').withMessage('Invalid DID'), - check('jobId').custom((value, {req})=>{ - if(!value && !(req.body.name && req.body.type && req.body.data)) return false - return true - }).withMessage('name, type and data are required'), - check('name').optional().isString().withMessage('Invalid name'), - check('type').optional().isString().withMessage('Invalid type'), - check('data').optional().isString().withMessage('Invalid data'), + check('name').exists().withMessage('name is required').isString().withMessage('Invalid name'), + check('type').exists().withMessage('type is required').isString().withMessage('Invalid type'), + check('data').exists().withMessage('data is required').isString().withMessage('Invalid data'), + check('encoding').exists().withMessage('encoding is required') + .isString().isIn(['hex', 'base64', 'base64url']).withMessage('Invalid encoding'), check('alsoKnownAs').optional().isArray().withMessage('Invalid alsoKnownAs'), check('alsoKnownAs.*.uri').isString().withMessage('Invalid uri'), check('alsoKnownAs.*.description').isString().withMessage('Invalid description') @@ -84,32 +78,39 @@ export class IssuerController { }) } - const { options, secret } = request.body - const { methodSpecificIdAlgo, network, versionId = v4()} = options - const verificationMethod = secret?.verificationMethod + const { methodSpecificIdAlgo, network, verificationMethodType, assertionMethod=true, serviceEndpoint } = request.body let didDocument: DIDDocument - let kids: string[] = [] try { - if (options.didDocument) { - didDocument = options.didDocument - } else if (verificationMethod) { + if (request.body.didDocument) { + didDocument = request.body.didDocument + } else if (verificationMethodType) { const key = await Identity.instance.createKey('Ed25519', response.locals.customerId) - kids.push(key.kid) didDocument = generateDidDoc({ - verificationMethod: verificationMethod.type, - verificationMethodId: verificationMethod.id || 'key-1', + verificationMethod: verificationMethodType || VerificationMethods.Ed255192018, + verificationMethodId: 'key-1', methodSpecificIdAlgo: (methodSpecificIdAlgo as MethodSpecificIdAlgo) || MethodSpecificIdAlgo.Uuid, network, publicKey: key.publicKeyHex }) - didDocument.assertionMethod = didDocument.authentication + + if (assertionMethod) { + didDocument.assertionMethod = didDocument.authentication + } + + if (serviceEndpoint) { + didDocument.service = [{ + id: `${didDocument.id}#service-1`, + type: 'service-1', + serviceEndpoint + }] + } } else { return response.status(400).json({ - error: 'Provide a DID Document or atleast one verification method' + error: 'Provide a DID Document or the network type to create a DID' }) } - const did = await Identity.instance.createDid(network, didDocument, response.locals.customerId) + const did = await Identity.instance.createDid(network || didDocument.id.split(':')[2], didDocument, response.locals.customerId) return response.status(200).json(did) } catch (error) { return response.status(500).json({ @@ -127,8 +128,33 @@ export class IssuerController { } try { - const did = await Identity.instance.updateDid(request.body.didDocument, response.locals.customerId) - return response.status(200).json(did) + + const { did, service, verificationMethod, authentication } = request.body + let updatedDocument: DIDDocument + if (request.body.didDocument) { + updatedDocument = request.body.didDocument + } else if (did && (service || verificationMethod || authentication)) { + let resolvedDocument: any = await Identity.instance.resolveDid(did) + if(!resolvedDocument?.didDocument || resolvedDocument.didDocumentMetadata.deactivated) { + return response.status(400).send({ + error: `${did} is either Deactivated or Not found` + }) + } else { + resolvedDocument = resolvedDocument.didDocument + } + if (service) resolvedDocument.service = service + if (verificationMethod) resolvedDocument.verificationMethod = verificationMethod + if (authentication) resolvedDocument.authentication = authentication + + updatedDocument = resolvedDocument + } else { + return response.status(400).json({ + error: 'Provide a DID Document or atleast one field to update' + }) + } + + const result = await Identity.instance.updateDid(updatedDocument, response.locals.customerId) + return response.status(200).json(result) } catch (error) { return response.status(500).json({ error: `${error}` @@ -163,7 +189,7 @@ export class IssuerController { } const { did } = request.params - let { data, name, type, alsoKnownAs, version, network } = request.body + let { data, encoding, name, type, alsoKnownAs, version, network } = request.body let resourcePayload: Partial = {} try { @@ -171,7 +197,7 @@ export class IssuerController { let resolvedDocument: any = await Identity.instance.resolveDid(did) if(!resolvedDocument?.didDocument || resolvedDocument.didDocumentMetadata.deactivated) { return response.status(400).send({ - error: `${did} is a Deactivated DID` + error: `${did} is a either Deactivated or Not found` }) } else { resolvedDocument = resolvedDocument.didDocument @@ -182,7 +208,7 @@ export class IssuerController { id: v4(), name, resourceType: type, - data: fromString(data, 'base64'), + data: fromString(data, encoding), version, alsoKnownAs } diff --git a/src/controllers/revocation.ts b/src/controllers/revocation.ts index a222421a..4036974c 100644 --- a/src/controllers/revocation.ts +++ b/src/controllers/revocation.ts @@ -16,7 +16,7 @@ export class RevocationController { ] static queryValidator = [ - query('did').isString().withMessage('DID is required') + check('did').isString().withMessage('DID is required') .contains('did:cheqd:').withMessage('Provide a valid cheqd DID'), query('statusPurpose').optional().isString().withMessage('statusPurpose should be a string') .isIn(['suspension', 'revocation']).withMessage('Invalid statuspurpose'), @@ -29,11 +29,11 @@ export class RevocationController { return response.status(400).json({ error: result.array()[0].msg }) } - let { length, encoding } = request.body - let { data, name, statusPurpose, alsoKnownAs, version } = request.body + let { data, name, alsoKnownAs, version, length, encoding } = request.body + const { statusPurpose } = request.query as { statusPurpose: 'revocation' | 'suspension' } const did = request.query.did as string - data = data ? fromString(data, 'base64') : undefined + data = data ? fromString(data, encoding) : undefined try { let result: any diff --git a/src/static/swagger.json b/src/static/swagger.json index 35bc6316..01988583 100644 --- a/src/static/swagger.json +++ b/src/static/swagger.json @@ -130,7 +130,7 @@ "DID" ], "summary": "Create a DID", - "description": "

This endpoint creates a DID by taking DID document or a verification method as an input.

", + "description": "

This endpoint creates a DID by taking a set of input parameters or the whole didDocument itself

", "security": [ { "bearerAuth": [] @@ -199,7 +199,7 @@ "DID" ], "summary": "Update a DID", - "description": "

This endpoint updates a DID by taking DID document or a verification method as an input.

", + "description": "

This endpoint updates a DID by taking DID document or the particular fields needed to be updated

", "security": [ { "bearerAuth": [] @@ -333,7 +333,7 @@ "DID" ], "summary": "Fetch DIDs from wallet", - "description": "

This endpoint creates a DID by taking DID document as an input.

", + "description": "

This endpoint returns the list of DIDs controlled by the account

", "security": [ { "bearerAuth": [] @@ -390,8 +390,8 @@ "tags": [ "DID" ], - "summary": "Fetch DIDs from wallet", - "description": "

This endpoint creates a DID by taking DID document as an input.

", + "summary": "Resolve a DID", + "description": "

This endpoint resolved a DID

", "security": [ { "bearerAuth": [] @@ -456,7 +456,7 @@ "Credential" ], "summary": "Issue a credential", - "description": "

This endpoint creates a DID. As input it takes the list of attributes, subjectDid, context and expiration date of the credential to be issued.

", + "description": "

This endpoint issues a credential. As input it takes the list of attributes, subjectDid, context and expiration date of the credential to be issued.

", "security": [ { "bearerAuth": [] @@ -622,7 +622,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/credentialStatusResult" + "$ref": "#/components/schemas/CredentialStatusResult" } } } @@ -816,22 +816,12 @@ "content": { "application/x-www-form-urlencoded": { "schema": { - "type": "object", - "properties": { - "presentation": { - "type": "object" - } - } + "$ref": "#/components/schemas/PresentationRequest" } }, "application/json": { "schema": { - "type": "object", - "properties": { - "presentation": { - "type": "object" - } - } + "$ref": "#/components/schemas/PresentationRequest" } } } @@ -1179,14 +1169,6 @@ ], "summary": "Create statuslist 2021", "parameters": [ - { - "in": "query", - "name": "did", - "required": true, - "schema": { - "type": "string" - } - }, { "in": "query", "name": "statusPurpose", @@ -1344,14 +1326,6 @@ ], "summary": "Publish statuslist 2021", "parameters": [ - { - "in": "query", - "name": "did", - "required": true, - "schema": { - "type": "string" - } - }, { "in": "query", "name": "statusPurpose", @@ -1576,7 +1550,7 @@ } }, "DidDocument": { - "description": "This input field contains either a complete DID document, or an incremental change (diff) to a DID document. See https://identity.foundation/did-registration/#diddocument.", + "description": "This input field contains a complete DID document", "type": "object", "properties": { "context": { @@ -1725,24 +1699,29 @@ "type": "string" }, "attributes": { + "description": "Json input of the attributes", "type": "object" }, "@context": { + "description": "Additional contexts to be included in the credential", "type": "array", "items": { "type": "string" } }, "type": { + "description": "Additional type property to be included in the credential", "type": "array", "items": { "type": "string" } }, "expirationDate": { + "description": "Optional expiration date according to the specification", "type": "string" }, "format": { + "description": "Select one of the supported credential formats, jwt by default", "type": "string", "enum": [ "jwt", @@ -1750,6 +1729,7 @@ ] }, "credentialStatus": { + "description": "Optional field to support revocation or suspension, which takes statusListName and statusListPurpose as inputs.", "type": "object", "required": [ "statusPurpose", @@ -1972,9 +1952,7 @@ }, "didDocumentMetadata": { "created": "2023-06-07T15:53:59.629128532Z", - "versionId": "8e286b55-7a25-44bb-af93-f0f8fd9df1b7" - }, - "didResolutionMetadata": { + "versionId": "8e286b55-7a25-44bb-af93-f0f8fd9df1b7", "contentType": "application/did+ld+json", "did": { "didString": "did:cheqd:testnet:7bf81a20-633c-4cc7-bc4a-5a45801005e0", @@ -2024,14 +2002,32 @@ "description": "Input fields for the resource creation", "type": "object", "additionalProperties": false, + "required": [ + "name", + "type", + "data", + "encoding" + ], "properties": { "data": { + "description": "Provide encoded string for the resource data", "type": "string" }, + "encoding": { + "description": "The encoding format of the resource data", + "type": "string", + "enum": [ + "base64url", + "base64", + "hex" + ] + }, "name": { + "description": "Resource name", "type": "string" }, "type": { + "description": "Resource type", "type": "string" }, "alsoKnownAs": { @@ -2058,7 +2054,7 @@ "type": "TextDocument" } }, - "credentialStatusResult": { + "CredentialStatusResult": { "properties": { "revoked": { "type": "boolean" @@ -2087,50 +2083,36 @@ }, "DIDCreateRequest": { "type": "object", - "required": [ - "options" - ], "properties": { - "options": { - "type": "object", - "properties": { - "network": { - "type": "string", - "enum": [ - "testnet", - "mainnet" - ] - }, - "methodSpecificIdAlgo": { - "type": "string", - "enum": [ - "uuid", - "base58btc" - ] - } - } + "network": { + "type": "string", + "enum": [ + "testnet", + "mainnet" + ] }, - "secret": { - "type": "object", - "properties": { - "verificationMethod": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "Ed25519VerificationKey2018", - "JsonWebKey2020", - "Ed25519VerificationKey2020" - ] - }, - "id": { - "type": "string", - "example": "key-1" - } - } - } - } + "methodSpecificIdAlgo": { + "type": "string", + "enum": [ + "uuid", + "base58btc" + ] + }, + "verificationMethodType": { + "type": "string", + "enum": [ + "Ed25519VerificationKey2018", + "JsonWebKey2020", + "Ed25519VerificationKey2020" + ] + }, + "serviceEndpoint": { + "type": "string" + }, + "assertionMethod": { + "description": "An assertion method is required to issue JSONLD credentials", + "type": "boolean", + "default": true }, "didDocument": { "$ref": "#/components/schemas/DidDocument" @@ -2139,10 +2121,28 @@ }, "DIDUpdateRequest": { "type": "object", - "required": [ - "didDocument" - ], "properties": { + "did": { + "type": "string" + }, + "service": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Service" + } + }, + "verificationMethod": { + "type": "array", + "items": { + "$ref": "#/components/schemas/VerificationMethod" + } + }, + "authentication": { + "type": "array", + "items": { + "type": "string" + } + }, "didDocument": { "$ref": "#/components/schemas/DidDocument" } @@ -2152,8 +2152,15 @@ "type": "object", "properties": { "credential": { - "type": "object", - "example": "" + "description": "This field takes the credential object or the JWT as input", + "oneOf": [ + { + "type": "object" + }, + { + "type": "string" + } + ] } } }, @@ -2161,7 +2168,15 @@ "type": "object", "properties": { "credential": { - "type": "object" + "description": "This field takes the credential object or the JWT as input", + "oneOf": [ + { + "type": "object" + }, + { + "type": "string" + } + ] }, "publish": { "type": "boolean", @@ -2171,57 +2186,135 @@ }, "CredentialStatusCreateRequest": { "allOf": [ - { - "$ref": "#/components/schemas/CreateResourceRequest" - }, { "type": "object", + "required": [ + "did", + "name" + ], "properties": { + "did": { + "description": "The DID of the statuslist publisher", + "type": "string" + }, + "name": { + "type": "string" + }, "length": { "type": "number" }, "encoding": { + "description": "The encoding format of the statusList to be published", "type": "string", "enum": [ "base64url", "base64", "hex" ] + }, + "version": { + "type": "string" + }, + "alsoKnownAs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uri": { + "type": "string" + }, + "description": { + "type": "string" + } + } + } } - }, - "example": { - "length": 140000, - "name": "cheqd-employee-credentials", - "version": "2023" } } - ] + ], + "example": { + "did": "did:cheqd:testnet:7c2b990c-3d05-4ebf-91af-f4f4d0091d2e", + "length": 140000, + "name": "cheqd-employee-credentials", + "version": "2023" + } }, "CredentialStatusPublishRequest": { "allOf": [ - { - "$ref": "#/components/schemas/CreateResourceRequest" - }, { "type": "object", + "required": [ + "did", + "data", + "name", + "encoding" + ], "properties": { + "did": { + "description": "The DID of the statuslist publisher", + "type": "string" + }, + "name": { + "type": "string" + }, + "data": { + "description": "Provide encoded string for the resource data", + "type": "string" + }, "encoding": { + "description": "The encoding format of the statusList to be published", "type": "string", "enum": [ "base64url", "base64", "hex" ] + }, + "version": { + "type": "string" + }, + "alsoKnownAs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uri": { + "type": "string" + }, + "description": { + "type": "string" + } + } + } } } } ], "example": { + "did": "did:cheqd:testnet:7c2b990c-3d05-4ebf-91af-f4f4d0091d2e", "name": "cheqd-employee-credentials", "version": "2023", "data": "H4sIAAAAAAAAA-3BAQ0AAADCoPdPbQ8HFAAAAAAAAAAAAAAAAAAAAADwaDhDr_xcRAAA", "encoding": "base64url" } + }, + "PresentationRequest": { + "type": "object", + "required": [ + "presentation" + ], + "properties": { + "presentation": { + "oneOf": [ + { + "type": "object" + }, + { + "type": "string" + } + ] + } + } } } }