diff --git a/src/app.js b/src/app.js index 0268a75..0952096 100644 --- a/src/app.js +++ b/src/app.js @@ -68,7 +68,7 @@ export async function build(opts = {}) { await verifyAuthHeader(authHeader, tenantName) // NOTE: we throw the error here which will then be caught by middleware errorhandler - if (!req.body || !Object.keys(req.body).length) throw {code:400, message:'A verifiable credential must be provided in the body'} + if (!unSignedVC || !Object.keys(unSignedVC).length) throw {code:400, message:'A verifiable credential must be provided in the body'} const vcWithStatus = enableStatusService ? await callService(`http://${statusServiceEndpoint}/credentials/status/allocate`, unSignedVC) : @@ -93,10 +93,17 @@ export async function build(opts = {}) { const authHeader = req.headers.authorization const statusUpdate = req.body await verifyAuthHeader(authHeader, tenantName) - const updateResult = await callService(`http://${statusServiceEndpoint}/credentials/status`, statusUpdate) + // NOTE: we throw the error here which will then be caught by middleware errorhandler + if (!statusUpdate || !Object.keys(statusUpdate).length) throw {code:400, message:'A status update must be provided in the body.'} + const updateResult = await callService(`http://${statusServiceEndpoint}/credentials/status`, statusUpdate) return res.json(updateResult) } catch (error) { - // have to catch and forward async errors to middleware: + if (error.response?.status == 404) { + // if it is a 404 then just forward on the error + // we got from the service + next(error.response.data) + } + // otherwise, forward the error to middleware: next(error) } }) diff --git a/src/app.test.js b/src/app.test.js index a94f169..e1d4125 100644 --- a/src/app.test.js +++ b/src/app.test.js @@ -1,34 +1,12 @@ import nock from 'nock'; -import axios from 'axios' import { expect } from 'chai' -import { dirname } from 'path'; import request from 'supertest'; -import { fileURLToPath } from 'url'; import { getUnsignedVC, getUnsignedVCWithStatus } from './test-fixtures/vc.js'; -import unsignedNock from './test-fixtures/nocks/unprotected_sign.js' - - -axios.defaults.adapter = 'http' -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); -nock.back.fixtures = __dirname + '/nockBackFixtures' -let saveNockRecording; - - - -async function startNockBackRecording(fixtureFileName) { - nock.back.setMode('wild') - const { nockDone } = await nock.back('nockMocks.json'); - saveNockRecording = nockDone - // allow the requests to localhost, i.e, the test calls themselves - //nock.enableNetConnect(/127\.0\.0\.1/); - //nock.enableNetConnect(/localhost/); -} - -async function stopAndSaveNockRecording() { - saveNockRecording() - //nock.back.setMode('wild') -} +import unprotectedNock from './test-fixtures/nocks/unprotected_status_signing.js' +import protectedNock from './test-fixtures/nocks/protected_status_signing.js' +import unprotectedStatusUpdateNock from './test-fixtures/nocks/unprotected_status_update.js' +import unknownStatusIdNock from './test-fixtures/nocks/unknown_status_id_nock.js' +import protectedStatusUpdateNock from './test-fixtures/nocks/protected_status_update.js' import { build } from './app.js'; @@ -42,24 +20,17 @@ describe('api', () => { before(async () => { //testDIDSeed = await decodeSeed(process.env.TENANT_SEED_TESTING) - testTenantToken = process.env.TENANT_TOKEN_TESTING - testTenantToken2 = process.env.TENANT_TOKEN_TESTING_2 - - //didDocument = (await didKeyDriver.generate({ seed: testDIDSeed })).didDocument - //verificationMethod = didKeyDriver.publicMethodFor({ didDocument, purpose: 'assertionMethod' }).id - //signingDID = didDocument.id + testTenantToken = process.env.TENANT_TOKEN_PROTECTED_TEST + testTenantToken2 = process.env.TENANT_TOKEN_PROTECTED_TEST_2 statusUpdateBody = { "credentialId": "urn:uuid:951b475e-b795-43bc-ba8f-a2d01efd2eb1", "credentialStatus": [{ "type": "StatusList2021Credential", "status": "revoked" }] } - - //startNockBackRecording() }); after(() => { - //stopAndSaveNockRecording() }) beforeEach(async () => { app = await build(); - + if (!nock.isActive()) nock.activate() }); afterEach(async () => { @@ -93,7 +64,7 @@ describe('api', () => { it('returns 400 if no body', done => { request(app) - .post("/instance/testing/credentials/issue") + .post("/instance/protected_test/credentials/issue") .set('Authorization', `Bearer ${testTenantToken}`) .expect('Content-Type', /json/) .expect(400, done) @@ -101,18 +72,17 @@ describe('api', () => { it('returns 401 if tenant token is missing from auth header', done => { request(app) - .post("/instance/testing/credentials/issue") + .post("/instance/protected_test/credentials/issue") .send(getUnsignedVC()) .expect('Content-Type', /json/) .expect(401, done) }) - it.only('issues credential for UNPROTECTED tenant, without auth header', async () => { - //nock.recorder.rec() - unsignedNock(); + it('issues credential for unprotected tenant', async () => { + unprotectedNock(); const response = await request(app) - .post("/instance/testing3/credentials/issue") + .post("/instance/un_protected_test/credentials/issue") .send(getUnsignedVC()) expect(response.header["content-type"]).to.have.string("json"); @@ -124,28 +94,28 @@ describe('api', () => { it('returns 403 if token is not valid', done => { request(app) - .post("/instance/testing/credentials/issue") + .post("/instance/protected_test/credentials/issue") .set('Authorization', `Bearer badToken`) .send(getUnsignedVC()) - .expect('Content-Type', /text/) + .expect('Content-Type', /json/) .expect(403, done) }) it('returns 403 when trying to use token for a different tenant', done => { request(app) - .post("/instance/testing/credentials/issue") + .post("/instance/protected_test/credentials/issue") .set('Authorization', `Bearer ${testTenantToken2}`) .send(getUnsignedVC()) - .expect('Content-Type', /text/) + .expect('Content-Type', /json/) .expect(403, done) }) it('returns 401 if token is not marked as Bearer', done => { request(app) - .post("/instance/testing/credentials/issue") + .post("/instance/protected_test/credentials/issue") .set('Authorization', `${testTenantToken}`) .send(getUnsignedVC()) - .expect('Content-Type', /text/) + .expect('Content-Type', /json/) .expect(401, done) }) @@ -155,21 +125,16 @@ describe('api', () => { .set('Authorization', `${testTenantToken}`) .send(getUnsignedVC()) .expect(404, done) - .expect('Content-Type', /text/) + .expect('Content-Type', /json/) }) - it('invokes the signing service', async () => {}) - - it('invokes the status service', async () => {}) - - - it('returns the vc from signing service', async () => { - // get the returned VC from the nock, once we've run nock-back. - const credFromSigningService = "will get from nock." - const sentCred = getUnsignedVCWithStatus() + it('returns signed vc for protected tenant', async () => { + //nock.recorder.rec() + protectedNock() + const sentCred = getUnsignedVC() const response = await request(app) - .post("/instance/testing/credentials/issue") + .post("/instance/protected_test/credentials/issue") .set('Authorization', `Bearer ${testTenantToken}`) .send(sentCred) @@ -177,7 +142,8 @@ describe('api', () => { expect(response.status).to.eql(200); const returnedCred = JSON.parse(JSON.stringify(response.body)); - expect(credFromSigningService).to.eql(returnedCred) + // this proof value comes from the nock: + expect(returnedCred.proof.proofValue).to.eql("z5QQ12zr5JvEsKvbnEN2EYZ6punR6Pa5wMJzywGJ2dCh6SSA5oQb9hBiGADsNTbs57bopArwdBHE9kEVemMxcu1Fq") }); @@ -187,43 +153,45 @@ describe('api', () => { it('returns 400 if no body', done => { request(app) - .post("/instance/testing/credentials/status") + .post("/instance/un_protected_test/credentials/status") .set('Authorization', `Bearer ${testTenantToken}`) - .expect('Content-Type', /text/) + .expect('Content-Type', /json/) .expect(400, done) }) it('returns 401 if tenant token is missing from auth header', done => { request(app) - .post("/instance/testing/credentials/status") + .post("/instance/protected_test/credentials/status") .send(statusUpdateBody) - .expect('Content-Type', /text/) + .expect('Content-Type', /json/) .expect(401, done) }) - it('no auth header needed to update status when token not set for tenant in config', done => { + it('update unprotected status when token not set for tenant in config', done => { + //nock.recorder.rec() + unprotectedStatusUpdateNock() request(app) - .post("/instance/testing3/credentials/status") + .post("/instance/un_protected_test/credentials/status") .send(statusUpdateBody) - .expect('Content-Type', /text/) + .expect('Content-Type', /json/) .expect(200, done) }) it('returns 403 if token is not valid', done => { request(app) - .post("/instance/testing/credentials/status") + .post("/instance/protected_test/credentials/status") .set('Authorization', `Bearer ThisIsABadToken`) .send(statusUpdateBody) - .expect('Content-Type', /text/) + .expect('Content-Type', /json/) .expect(403, done) }) it('returns 401 if token is not marked as Bearer', done => { request(app) - .post("/instance/testing/credentials/status") + .post("/instance/protected_test/credentials/status") .set('Authorization', `${testTenantToken}`) .send(statusUpdateBody) - .expect('Content-Type', /text/) + .expect('Content-Type', /json/) .expect(401, done) }) @@ -233,40 +201,43 @@ describe('api', () => { .set('Authorization', `${testTenantToken}`) .send(statusUpdateBody) .expect(404, done) - .expect('Content-Type', /text/) + .expect('Content-Type', /json/) }) it('returns 403 when trying to use token for a different tenant', done => { request(app) - .post("/instance/testing/credentials/status") + .post("/instance/protected_test/credentials/status") .set('Authorization', `Bearer ${testTenantToken2}`) .send(statusUpdateBody) - .expect('Content-Type', /text/) + .expect('Content-Type', /json/) .expect(403, done) }) - it('returns 404 for unknown cred id', done => { - // it wil have gotten the 404 from the status service and then - // simply returned that. + it('returns 404 for unknown cred id', async () => { + // nock.recorder.rec() + unknownStatusIdNock() const statusUpdateBodyWithUnknownId = JSON.parse(JSON.stringify(statusUpdateBody)) statusUpdateBodyWithUnknownId.credentialId = 'kj09ij' - request(app) - .post("/instance/testing/credentials/status") + const response = await request(app) + .post("/instance/protected_test/credentials/status") .set('Authorization', `Bearer ${testTenantToken}`) .send(statusUpdateBodyWithUnknownId) - .expect('Content-Type', /text/) - .expect(404, done) + + + expect(response.header["content-type"]).to.have.string("json"); + expect(response.status).to.eql(404); }) - // AND A TEST FOR THE GENERAL BAD REQUEST THAT DOESN'T FALL INTO THE OTHER CATEGORIES. + + it('calls status manager for protected tenant', async () => { - it('calls status manager', async () => { + protectedStatusUpdateNock() const response = await request(app) - .post("/instance/testing/credentials/status") + .post("/instance/protected_test/credentials/status") .set('Authorization', `Bearer ${testTenantToken}`) .send(statusUpdateBody) - expect(response.header["content-type"]).to.have.string("text"); + expect(response.header["content-type"]).to.have.string("json"); expect(response.status).to.eql(200); }) diff --git a/src/middleware/errorHandler.js b/src/middleware/errorHandler.js index 9a32668..c661188 100644 --- a/src/middleware/errorHandler.js +++ b/src/middleware/errorHandler.js @@ -1,6 +1,6 @@ -import axios, { isAxiosError } from 'axios'; +import { isAxiosError } from 'axios'; function handleAxiosError(error) { - const errorResponse = { serviceConfig: error.config, code: 500 } + const errorResponse = { code: 500 } if (error.response) { // The microservice responded with a status code other than 2xx errorResponse.message = "One of the internal microservices returned an error" @@ -8,8 +8,8 @@ function handleAxiosError(error) { errorResponse.serviceResponseStatus = error.response.status errorResponse.serviceResponseHeaders = error.response.headers } else if (error.request) { - errorResponse.message = "One of the internal microservices didn't respond." - errorResponse.serviceRequest = error.request + errorResponse.message = "One of the internal microservices didn't respond. Hit / to check heartbeats." + // errorResponse.serviceRequest = error.request } else { errorResponse.message = `Likely an error when setting up the request that prevented the request from being formulated: ${error.message}` errorResponse.error = error @@ -22,9 +22,8 @@ const errorHandler = (error, request, response, next) => { if (isAxiosError(error)) { errorResponse = handleAxiosError(error); } else { - // this is an error returned for something internal, - // like the check for an access token, - // or a missing argument on a call + // otherwise, this is an error that we've thrown ourselves, + // or it is some other error, so pass it along errorResponse = error } response.header("Content-Type", 'application/json') diff --git a/src/middleware/errorLogger.js b/src/middleware/errorLogger.js index 13ddabd..3b66180 100644 --- a/src/middleware/errorLogger.js +++ b/src/middleware/errorLogger.js @@ -1,30 +1,31 @@ -import axios, {isAxiosError} from 'axios'; +import {isAxiosError} from 'axios'; import logger from "../utils/logger.js"; const errorLogger = (error, request, response, next) => { + const logEntry = {} - + if (isAxiosError(error)) { // an error when calling one of the microservices logEntry.serviceConfig = error.config if (error.response) { // The microservice responded with a status code other than 2xx - logEntry.message = "One of the internal microservices returned an error" + logEntry.message = "One of the internal microservices returned an error." logEntry.serviceResponseError = error.response.data logEntry.serviceResponseStatus = error.response.status logEntry.serviceResponseHeaders = error.response.headers } else if (error.request) { - logEntry.message = "One of the internal microservices didn't respond." - logEntry.serviceRequest = error.request + logEntry.message = "One of the internal microservices didn't respond. Hit / to check heartbeats." } else { logEntry.message = `Likely an error when setting up a request to one of the internal microservices that prevented the request from being formulated: ${error.message}` logEntry.error = error } } else { // a non-axios error - console.log(error) + logEntry.message = error.message || "An unknown error occurred." } - const errorLogMessage = `${error.statusCode} || ${response.statusMessage} - ${request.originalUrl} - ${request.method} - ${request.ip} - ${error.message}` + + const errorLogMessage = `Error for route: ${request.originalUrl} - ${request.method} - IP: ${request.ip}` // note that the logEntry here is what Winston calls a 'meta' object and it simply // output to the log as provided, as JSON in this case. diff --git a/src/test-fixtures/.env.testing b/src/test-fixtures/.env.testing index 3181fdc..ec3b1d6 100644 --- a/src/test-fixtures/.env.testing +++ b/src/test-fixtures/.env.testing @@ -3,10 +3,12 @@ #ENABLE_HTTPS_FOR_DEV=false SIGNING_SERVICE_ENDPOINT=localhost:4006 STATUS_SERVICE_ENDPOINT=localhost:4008 -ENABLE_STATUS_SERVICE=true +ENABLE_STATUS_SERVICE=true + # we deliberately don't set a token for the third tenant to test that the call is still allowed # i.e,. we want to allow some tenants to work without a token. -TENANT_TOKEN_TESTING=ohno -TENANT_TOKEN_TESTING2=YAAAAAA -TENANT_TOKEN_TESTING3=UNPROTECTED +TENANT_TOKEN_UN_PROTECTED_TEST=UNPROTECTED +TENANT_TOKEN_PROTECTED_TEST=jds +TENANT_TOKEN_PROTECTED_TEST_2=hgf +TENANT_TOKEN_RANDOM_TESTING=UNPROTECTED diff --git a/src/test-fixtures/nocks/protected_status_signing.js b/src/test-fixtures/nocks/protected_status_signing.js new file mode 100644 index 0000000..432ca8a --- /dev/null +++ b/src/test-fixtures/nocks/protected_status_signing.js @@ -0,0 +1,47 @@ +import nock from 'nock'; +const signedVC = {"@context":["https://www.w3.org/2018/credentials/v1","https://purl.imsglobal.org/spec/ob/v3p0/context.json","https://w3id.org/vc/status-list/2021/v1","https://w3id.org/security/suites/ed25519-2020/v1","https://w3id.org/vc/status-list/2021/v1"],"id":"urn:uuid:951b475e-b795-43bc-ba8f-a2d01efd2eb1","type":["VerifiableCredential","OpenBadgeCredential"],"issuer":{"id":"did:key:z6MkrDLfZytHX5acEFds6wAYJbvMbT9UuhKhub4qaguDESCF","type":"Profile","name":"Dr David Malan","description":"Gordon McKay Professor of the Practice of Computer Science, Harvard University","url":"https://cs.harvard.edu/malan/","image":{"id":"https://certificates.cs50.io/static/success.jpg","type":"Image"}},"issuanceDate":"2020-01-01T00:00:00Z","name":"Introduction to Computer Science - CS50x","credentialSubject":{"type":"AchievementSubject","identifier":{"type":"IdentityObject","identityHash":"jc.chartrand@gmail.com","hashed":"false"},"achievement":{"id":"http://cs50.harvard.edu","type":"Achievement","criteria":{"narrative":"Completion of CS50X, including ten problem sets, ten labs, and one final project."},"description":"CS50 congratulates on completion of CS50x.","name":"Introduction to Computer Science - CS50x"}},"credentialStatus":{"id":"https://jchartrand.github.io/status-test-three/DKSPRCX9WB#1","type":"StatusList2021Entry","statusPurpose":"revocation","statusListIndex":1,"statusListCredential":"https://jchartrand.github.io/status-test-three/DKSPRCX9WB"},"proof":{"type":"Ed25519Signature2020","created":"2023-08-23T12:44:15Z","verificationMethod":"did:key:z6MkrDLfZytHX5acEFds6wAYJbvMbT9UuhKhub4qaguDESCF#z6MkrDLfZytHX5acEFds6wAYJbvMbT9UuhKhub4qaguDESCF","proofPurpose":"assertionMethod","proofValue":"z5QQ12zr5JvEsKvbnEN2EYZ6punR6Pa5wMJzywGJ2dCh6SSA5oQb9hBiGADsNTbs57bopArwdBHE9kEVemMxcu1Fq"}} + +export default () => { + nock('http://localhost:4008', {"encodedQueryParams":true}) + .post('/credentials/status/allocate', {"@context":["https://www.w3.org/2018/credentials/v1","https://purl.imsglobal.org/spec/ob/v3p0/context.json","https://w3id.org/vc/status-list/2021/v1","https://w3id.org/security/suites/ed25519-2020/v1"],"id":"urn:uuid:951b475e-b795-43bc-ba8f-a2d01efd2eb1","type":["VerifiableCredential","OpenBadgeCredential"],"issuer":{"id":"did:key:z6MkhVTX9BF3NGYX6cc7jWpbNnR7cAjH8LUffabZP8Qu4ysC","type":"Profile","name":"Dr David Malan","description":"Gordon McKay Professor of the Practice of Computer Science, Harvard University","url":"https://cs.harvard.edu/malan/","image":{"id":"https://certificates.cs50.io/static/success.jpg","type":"Image"}},"issuanceDate":"2020-01-01T00:00:00Z","name":"Introduction to Computer Science - CS50x","credentialSubject":{"type":"AchievementSubject","identifier":{"type":"IdentityObject","identityHash":"jc.chartrand@gmail.com","hashed":"false"},"achievement":{"id":"http://cs50.harvard.edu","type":"Achievement","criteria":{"narrative":"Completion of CS50X, including ten problem sets, ten labs, and one final project."},"description":"CS50 congratulates on completion of CS50x.","name":"Introduction to Computer Science - CS50x"}}}) + .reply(200, {"@context":["https://www.w3.org/2018/credentials/v1","https://purl.imsglobal.org/spec/ob/v3p0/context.json","https://w3id.org/vc/status-list/2021/v1","https://w3id.org/security/suites/ed25519-2020/v1","https://w3id.org/vc/status-list/2021/v1"],"id":"urn:uuid:951b475e-b795-43bc-ba8f-a2d01efd2eb1","type":["VerifiableCredential","OpenBadgeCredential"],"issuer":{"id":"did:key:z6MkhVTX9BF3NGYX6cc7jWpbNnR7cAjH8LUffabZP8Qu4ysC","type":"Profile","name":"Dr David Malan","description":"Gordon McKay Professor of the Practice of Computer Science, Harvard University","url":"https://cs.harvard.edu/malan/","image":{"id":"https://certificates.cs50.io/static/success.jpg","type":"Image"}},"issuanceDate":"2020-01-01T00:00:00Z","name":"Introduction to Computer Science - CS50x","credentialSubject":{"type":"AchievementSubject","identifier":{"type":"IdentityObject","identityHash":"jc.chartrand@gmail.com","hashed":"false"},"achievement":{"id":"http://cs50.harvard.edu","type":"Achievement","criteria":{"narrative":"Completion of CS50X, including ten problem sets, ten labs, and one final project."},"description":"CS50 congratulates on completion of CS50x.","name":"Introduction to Computer Science - CS50x"}},"credentialStatus":{"id":"https://jchartrand.github.io/status-test-three/DKSPRCX9WB#1","type":"StatusList2021Entry","statusPurpose":"revocation","statusListIndex":1,"statusListCredential":"https://jchartrand.github.io/status-test-three/DKSPRCX9WB"}}, [ + 'X-Powered-By', + 'Express', + 'Access-Control-Allow-Origin', + '*', + 'Content-Type', + 'application/json; charset=utf-8', + 'Content-Length', + '1470', + 'ETag', + 'W/"5be-fsduSOAlXIbTnkhg3Eo5U7uNYRQ"', + 'Date', + 'Wed, 23 Aug 2023 12:44:15 GMT', + 'Connection', + 'keep-alive', + 'Keep-Alive', + 'timeout=5' + ]); + + nock('http://localhost:4006', {"encodedQueryParams":true}) + .post('/instance/protected_test/credentials/sign', {"@context":["https://www.w3.org/2018/credentials/v1","https://purl.imsglobal.org/spec/ob/v3p0/context.json","https://w3id.org/vc/status-list/2021/v1","https://w3id.org/security/suites/ed25519-2020/v1","https://w3id.org/vc/status-list/2021/v1"],"id":"urn:uuid:951b475e-b795-43bc-ba8f-a2d01efd2eb1","type":["VerifiableCredential","OpenBadgeCredential"],"issuer":{"id":"did:key:z6MkhVTX9BF3NGYX6cc7jWpbNnR7cAjH8LUffabZP8Qu4ysC","type":"Profile","name":"Dr David Malan","description":"Gordon McKay Professor of the Practice of Computer Science, Harvard University","url":"https://cs.harvard.edu/malan/","image":{"id":"https://certificates.cs50.io/static/success.jpg","type":"Image"}},"issuanceDate":"2020-01-01T00:00:00Z","name":"Introduction to Computer Science - CS50x","credentialSubject":{"type":"AchievementSubject","identifier":{"type":"IdentityObject","identityHash":"jc.chartrand@gmail.com","hashed":"false"},"achievement":{"id":"http://cs50.harvard.edu","type":"Achievement","criteria":{"narrative":"Completion of CS50X, including ten problem sets, ten labs, and one final project."},"description":"CS50 congratulates on completion of CS50x.","name":"Introduction to Computer Science - CS50x"}},"credentialStatus":{"id":"https://jchartrand.github.io/status-test-three/DKSPRCX9WB#1","type":"StatusList2021Entry","statusPurpose":"revocation","statusListIndex":1,"statusListCredential":"https://jchartrand.github.io/status-test-three/DKSPRCX9WB"}}) + .reply(200, signedVC, [ + 'X-Powered-By', + 'Express', + 'Access-Control-Allow-Origin', + '*', + 'Content-Type', + 'application/json; charset=utf-8', + 'Content-Length', + '1810', + 'ETag', + 'W/"712-sK9DFAzLeARFlwoONQJ3mkiGzeU"', + 'Date', + 'Wed, 23 Aug 2023 12:44:15 GMT', + 'Connection', + 'keep-alive', + 'Keep-Alive', + 'timeout=5' +]); + +} diff --git a/src/test-fixtures/nocks/protected_status_update.js b/src/test-fixtures/nocks/protected_status_update.js new file mode 100644 index 0000000..35ca608 --- /dev/null +++ b/src/test-fixtures/nocks/protected_status_update.js @@ -0,0 +1,25 @@ +import nock from 'nock'; + +export default () => { + nock('http://localhost:4008', {"encodedQueryParams":true}) + .post('/credentials/status', {"credentialId":"urn:uuid:951b475e-b795-43bc-ba8f-a2d01efd2eb1","credentialStatus":[{"type":"StatusList2021Credential","status":"revoked"}]}) + .reply(200, {"code":200,"message":"Credential status successfully updated."}, [ + 'X-Powered-By', + 'Express', + 'Access-Control-Allow-Origin', + '*', + 'Content-Type', + 'application/json; charset=utf-8', + 'Content-Length', + '64', + 'ETag', + 'W/"40-QIRY/d4PUONYie1grSHLdg5/hcs"', + 'Date', + 'Wed, 23 Aug 2023 20:42:41 GMT', + 'Connection', + 'keep-alive', + 'Keep-Alive', + 'timeout=5' +]); + +} diff --git a/src/test-fixtures/nocks/unknown_status_id_nock.js b/src/test-fixtures/nocks/unknown_status_id_nock.js new file mode 100644 index 0000000..435eb48 --- /dev/null +++ b/src/test-fixtures/nocks/unknown_status_id_nock.js @@ -0,0 +1,24 @@ +import nock from 'nock'; + +export default () => { + nock('http://localhost:4008', {"encodedQueryParams":true}) + .post('/credentials/status', {"credentialId":"kj09ij","credentialStatus":[{"type":"StatusList2021Credential","status":"revoked"}]}) + .reply(404, {"code":404,"message":"Credential ID not found."}, [ + 'X-Powered-By', + 'Express', + 'Access-Control-Allow-Origin', + '*', + 'Content-Type', + 'application/json; charset=utf-8', + 'Content-Length', + '49', + 'ETag', + 'W/"31-lbV74I+iXZStkOsz/GAY1mIZewQ"', + 'Date', + 'Wed, 23 Aug 2023 20:44:24 GMT', + 'Connection', + 'keep-alive', + 'Keep-Alive', + 'timeout=5' +]); +} diff --git a/src/test-fixtures/nocks/unprotected_sign.js b/src/test-fixtures/nocks/unprotected_sign.js deleted file mode 100644 index c6d688f..0000000 --- a/src/test-fixtures/nocks/unprotected_sign.js +++ /dev/null @@ -1,22 +0,0 @@ -import nock from 'nock'; - -export default () => {nock('http://localhost:4006', {"encodedQueryParams":true}) - .post('/instance/testing3/credentials/sign', {"@context":["https://www.w3.org/2018/credentials/v1","https://purl.imsglobal.org/spec/ob/v3p0/context.json","https://w3id.org/vc/status-list/2021/v1","https://w3id.org/security/suites/ed25519-2020/v1","https://w3id.org/vc/status-list/2021/v1"],"id":"urn:uuid:951b475e-b795-43bc-ba8f-a2d01efd2eb1","type":["VerifiableCredential","OpenBadgeCredential"],"issuer":{"id":"did:key:z6MkhVTX9BF3NGYX6cc7jWpbNnR7cAjH8LUffabZP8Qu4ysC","type":"Profile","name":"Dr David Malan","description":"Gordon McKay Professor of the Practice of Computer Science, Harvard University","url":"https://cs.harvard.edu/malan/","image":{"id":"https://certificates.cs50.io/static/success.jpg","type":"Image"}},"issuanceDate":"2020-01-01T00:00:00Z","name":"Introduction to Computer Science - CS50x","credentialSubject":{"type":"AchievementSubject","identifier":{"type":"IdentityObject","identityHash":"jc.chartrand@gmail.com","hashed":"false"},"achievement":{"id":"http://cs50.harvard.edu","type":"Achievement","criteria":{"narrative":"Completion of CS50X, including ten problem sets, ten labs, and one final project."},"description":"CS50 congratulates on completion of CS50x.","name":"Introduction to Computer Science - CS50x"}},"credentialStatus":{"id":"https://jchartrand.github.io/status-test-three/DKSPRCX9WB#1","type":"StatusList2021Entry","statusPurpose":"revocation","statusListIndex":1,"statusListCredential":"https://jchartrand.github.io/status-test-three/DKSPRCX9WB"}}) - .reply(200, {"@context":["https://www.w3.org/2018/credentials/v1","https://purl.imsglobal.org/spec/ob/v3p0/context.json","https://w3id.org/vc/status-list/2021/v1","https://w3id.org/security/suites/ed25519-2020/v1","https://w3id.org/vc/status-list/2021/v1"],"id":"urn:uuid:951b475e-b795-43bc-ba8f-a2d01efd2eb1","type":["VerifiableCredential","OpenBadgeCredential"],"issuer":{"id":"did:key:z6Mkuoj16AELhDkUk8tvTLA6e6yenGXSNoZ5urtprJoqhuww","type":"Profile","name":"Dr David Malan","description":"Gordon McKay Professor of the Practice of Computer Science, Harvard University","url":"https://cs.harvard.edu/malan/","image":{"id":"https://certificates.cs50.io/static/success.jpg","type":"Image"}},"issuanceDate":"2020-01-01T00:00:00Z","name":"Introduction to Computer Science - CS50x","credentialSubject":{"type":"AchievementSubject","identifier":{"type":"IdentityObject","identityHash":"jc.chartrand@gmail.com","hashed":"false"},"achievement":{"id":"http://cs50.harvard.edu","type":"Achievement","criteria":{"narrative":"Completion of CS50X, including ten problem sets, ten labs, and one final project."},"description":"CS50 congratulates on completion of CS50x.","name":"Introduction to Computer Science - CS50x"}},"credentialStatus":{"id":"https://jchartrand.github.io/status-test-three/DKSPRCX9WB#1","type":"StatusList2021Entry","statusPurpose":"revocation","statusListIndex":1,"statusListCredential":"https://jchartrand.github.io/status-test-three/DKSPRCX9WB"},"proof":{"type":"Ed25519Signature2020","created":"2023-08-03T17:27:29Z","verificationMethod":"did:key:z6Mkuoj16AELhDkUk8tvTLA6e6yenGXSNoZ5urtprJoqhuww#z6Mkuoj16AELhDkUk8tvTLA6e6yenGXSNoZ5urtprJoqhuww","proofPurpose":"assertionMethod","proofValue":"z53EF47PshAVsVtRBTBBv8A1vJvWptWn5p4QupVnAeZYWZJnTGAcABmAVYRZ4CR1xAjWyPrg7ktXerJ9PfUgSLfTh"}}, [ - 'X-Powered-By', - 'Express', - 'Access-Control-Allow-Origin', - '*', - 'Content-Type', - 'application/json; charset=utf-8', - 'Content-Length', - '1810', - 'ETag', - 'W/"712-0nL+TtiN38hiHrSNvQHR9Iqira4"', - 'Date', - 'Thu, 03 Aug 2023 17:27:29 GMT', - 'Connection', - 'keep-alive', - 'Keep-Alive', - 'timeout=5' -])} \ No newline at end of file diff --git a/src/test-fixtures/nocks/unprotected_status_signing.js b/src/test-fixtures/nocks/unprotected_status_signing.js new file mode 100644 index 0000000..0eda79f --- /dev/null +++ b/src/test-fixtures/nocks/unprotected_status_signing.js @@ -0,0 +1,86 @@ +import nock from 'nock'; +const signedVC = {"@context":["https://www.w3.org/2018/credentials/v1","https://purl.imsglobal.org/spec/ob/v3p0/context.json","https://w3id.org/vc/status-list/2021/v1","https://w3id.org/security/suites/ed25519-2020/v1","https://w3id.org/vc/status-list/2021/v1"],"id":"urn:uuid:951b475e-b795-43bc-ba8f-a2d01efd2eb1","type":["VerifiableCredential","OpenBadgeCredential"],"issuer":{"id":"did:key:z6Mkf2rgv7ef8FmLJ5Py87LMa7nofQgv6AstdkgsXiiCUJEy","type":"Profile","name":"Dr David Malan","description":"Gordon McKay Professor of the Practice of Computer Science, Harvard University","url":"https://cs.harvard.edu/malan/","image":{"id":"https://certificates.cs50.io/static/success.jpg","type":"Image"}},"issuanceDate":"2020-01-01T00:00:00Z","name":"Introduction to Computer Science - CS50x","credentialSubject":{"type":"AchievementSubject","identifier":{"type":"IdentityObject","identityHash":"jc.chartrand@gmail.com","hashed":"false"},"achievement":{"id":"http://cs50.harvard.edu","type":"Achievement","criteria":{"narrative":"Completion of CS50X, including ten problem sets, ten labs, and one final project."},"description":"CS50 congratulates on completion of CS50x.","name":"Introduction to Computer Science - CS50x"}},"credentialStatus":{"id":"https://jchartrand.github.io/status-test-three/DKSPRCX9WB#1","type":"StatusList2021Entry","statusPurpose":"revocation","statusListIndex":1,"statusListCredential":"https://jchartrand.github.io/status-test-three/DKSPRCX9WB"},"proof":{"type":"Ed25519Signature2020","created":"2023-08-22T20:11:09Z","verificationMethod":"did:key:z6Mkf2rgv7ef8FmLJ5Py87LMa7nofQgv6AstdkgsXiiCUJEy#z6Mkf2rgv7ef8FmLJ5Py87LMa7nofQgv6AstdkgsXiiCUJEy","proofPurpose":"assertionMethod","proofValue":"z51uH32BFx2mNntaGE55MeHwespoAjetxDkTHBMKtbgGDdc5XiGSTaEGrRgANtT8DV5a6rTNnhT8FKRD4oVnhnxtG"}} + +export default () => { + + nock('http://localhost:4006', {"encodedQueryParams":true}) + .post('/instance/un_protected_test/credentials/sign', {"@context":["https://www.w3.org/2018/credentials/v1","https://purl.imsglobal.org/spec/ob/v3p0/context.json","https://w3id.org/vc/status-list/2021/v1","https://w3id.org/security/suites/ed25519-2020/v1","https://w3id.org/vc/status-list/2021/v1"],"id":"urn:uuid:951b475e-b795-43bc-ba8f-a2d01efd2eb1","type":["VerifiableCredential","OpenBadgeCredential"],"issuer":{"id":"did:key:z6MkhVTX9BF3NGYX6cc7jWpbNnR7cAjH8LUffabZP8Qu4ysC","type":"Profile","name":"Dr David Malan","description":"Gordon McKay Professor of the Practice of Computer Science, Harvard University","url":"https://cs.harvard.edu/malan/","image":{"id":"https://certificates.cs50.io/static/success.jpg","type":"Image"}},"issuanceDate":"2020-01-01T00:00:00Z","name":"Introduction to Computer Science - CS50x","credentialSubject":{"type":"AchievementSubject","identifier":{"type":"IdentityObject","identityHash":"jc.chartrand@gmail.com","hashed":"false"},"achievement":{"id":"http://cs50.harvard.edu","type":"Achievement","criteria":{"narrative":"Completion of CS50X, including ten problem sets, ten labs, and one final project."},"description":"CS50 congratulates on completion of CS50x.","name":"Introduction to Computer Science - CS50x"}},"credentialStatus":{"id":"https://jchartrand.github.io/status-test-three/DKSPRCX9WB#1","type":"StatusList2021Entry","statusPurpose":"revocation","statusListIndex":1,"statusListCredential":"https://jchartrand.github.io/status-test-three/DKSPRCX9WB"}}) + .reply(200, signedVC, [ + 'X-Powered-By', + 'Express', + 'Access-Control-Allow-Origin', + '*', + 'Content-Type', + 'application/json; charset=utf-8', + 'Content-Length', + '1810', + 'ETag', + 'W/"712-fUBsd5PM46QPKrivsShMP8gvwtc"', + 'Date', + 'Tue, 22 Aug 2023 20:11:09 GMT', + 'Connection', + 'keep-alive', + 'Keep-Alive', + 'timeout=5' +]); +nock('http://localhost:4008', {"encodedQueryParams":true}) + .post('/credentials/status/allocate', {"@context":["https://www.w3.org/2018/credentials/v1","https://purl.imsglobal.org/spec/ob/v3p0/context.json","https://w3id.org/vc/status-list/2021/v1","https://w3id.org/security/suites/ed25519-2020/v1"],"id":"urn:uuid:951b475e-b795-43bc-ba8f-a2d01efd2eb1","type":["VerifiableCredential","OpenBadgeCredential"],"issuer":{"id":"did:key:z6MkhVTX9BF3NGYX6cc7jWpbNnR7cAjH8LUffabZP8Qu4ysC","type":"Profile","name":"Dr David Malan","description":"Gordon McKay Professor of the Practice of Computer Science, Harvard University","url":"https://cs.harvard.edu/malan/","image":{"id":"https://certificates.cs50.io/static/success.jpg","type":"Image"}},"issuanceDate":"2020-01-01T00:00:00Z","name":"Introduction to Computer Science - CS50x","credentialSubject":{"type":"AchievementSubject","identifier":{"type":"IdentityObject","identityHash":"jc.chartrand@gmail.com","hashed":"false"},"achievement":{"id":"http://cs50.harvard.edu","type":"Achievement","criteria":{"narrative":"Completion of CS50X, including ten problem sets, ten labs, and one final project."},"description":"CS50 congratulates on completion of CS50x.","name":"Introduction to Computer Science - CS50x"}}}) + .reply(200, {"@context":["https://www.w3.org/2018/credentials/v1","https://purl.imsglobal.org/spec/ob/v3p0/context.json","https://w3id.org/vc/status-list/2021/v1","https://w3id.org/security/suites/ed25519-2020/v1","https://w3id.org/vc/status-list/2021/v1"],"id":"urn:uuid:951b475e-b795-43bc-ba8f-a2d01efd2eb1","type":["VerifiableCredential","OpenBadgeCredential"],"issuer":{"id":"did:key:z6MkhVTX9BF3NGYX6cc7jWpbNnR7cAjH8LUffabZP8Qu4ysC","type":"Profile","name":"Dr David Malan","description":"Gordon McKay Professor of the Practice of Computer Science, Harvard University","url":"https://cs.harvard.edu/malan/","image":{"id":"https://certificates.cs50.io/static/success.jpg","type":"Image"}},"issuanceDate":"2020-01-01T00:00:00Z","name":"Introduction to Computer Science - CS50x","credentialSubject":{"type":"AchievementSubject","identifier":{"type":"IdentityObject","identityHash":"jc.chartrand@gmail.com","hashed":"false"},"achievement":{"id":"http://cs50.harvard.edu","type":"Achievement","criteria":{"narrative":"Completion of CS50X, including ten problem sets, ten labs, and one final project."},"description":"CS50 congratulates on completion of CS50x.","name":"Introduction to Computer Science - CS50x"}},"credentialStatus":{"id":"https://jchartrand.github.io/status-test-three/DKSPRCX9WB#1","type":"StatusList2021Entry","statusPurpose":"revocation","statusListIndex":1,"statusListCredential":"https://jchartrand.github.io/status-test-three/DKSPRCX9WB"}}, [ + 'X-Powered-By', + 'Express', + 'Access-Control-Allow-Origin', + '*', + 'Content-Type', + 'application/json; charset=utf-8', + 'Content-Length', + '1470', + 'ETag', + 'W/"5be-fsduSOAlXIbTnkhg3Eo5U7uNYRQ"', + 'Date', + 'Tue, 22 Aug 2023 20:11:09 GMT', + 'Connection', + 'keep-alive', + 'Keep-Alive', + 'timeout=5' +]); + +/* nock('http://127.0.0.1:55225', {"encodedQueryParams":true}) + .post('/instance/un_protected_test/credentials/issue', {"@context":["https://www.w3.org/2018/credentials/v1","https://purl.imsglobal.org/spec/ob/v3p0/context.json","https://w3id.org/vc/status-list/2021/v1","https://w3id.org/security/suites/ed25519-2020/v1"],"id":"urn:uuid:951b475e-b795-43bc-ba8f-a2d01efd2eb1","type":["VerifiableCredential","OpenBadgeCredential"],"issuer":{"id":"did:key:z6MkhVTX9BF3NGYX6cc7jWpbNnR7cAjH8LUffabZP8Qu4ysC","type":"Profile","name":"Dr David Malan","description":"Gordon McKay Professor of the Practice of Computer Science, Harvard University","url":"https://cs.harvard.edu/malan/","image":{"id":"https://certificates.cs50.io/static/success.jpg","type":"Image"}},"issuanceDate":"2020-01-01T00:00:00Z","name":"Introduction to Computer Science - CS50x","credentialSubject":{"type":"AchievementSubject","identifier":{"type":"IdentityObject","identityHash":"jc.chartrand@gmail.com","hashed":"false"},"achievement":{"id":"http://cs50.harvard.edu","type":"Achievement","criteria":{"narrative":"Completion of CS50X, including ten problem sets, ten labs, and one final project."},"description":"CS50 congratulates on completion of CS50x.","name":"Introduction to Computer Science - CS50x"}}}) + .reply(200, {"@context":["https://www.w3.org/2018/credentials/v1","https://purl.imsglobal.org/spec/ob/v3p0/context.json","https://w3id.org/vc/status-list/2021/v1","https://w3id.org/security/suites/ed25519-2020/v1","https://w3id.org/vc/status-list/2021/v1"],"id":"urn:uuid:951b475e-b795-43bc-ba8f-a2d01efd2eb1","type":["VerifiableCredential","OpenBadgeCredential"],"issuer":{"id":"did:key:z6Mkf2rgv7ef8FmLJ5Py87LMa7nofQgv6AstdkgsXiiCUJEy","type":"Profile","name":"Dr David Malan","description":"Gordon McKay Professor of the Practice of Computer Science, Harvard University","url":"https://cs.harvard.edu/malan/","image":{"id":"https://certificates.cs50.io/static/success.jpg","type":"Image"}},"issuanceDate":"2020-01-01T00:00:00Z","name":"Introduction to Computer Science - CS50x","credentialSubject":{"type":"AchievementSubject","identifier":{"type":"IdentityObject","identityHash":"jc.chartrand@gmail.com","hashed":"false"},"achievement":{"id":"http://cs50.harvard.edu","type":"Achievement","criteria":{"narrative":"Completion of CS50X, including ten problem sets, ten labs, and one final project."},"description":"CS50 congratulates on completion of CS50x.","name":"Introduction to Computer Science - CS50x"}},"credentialStatus":{"id":"https://jchartrand.github.io/status-test-three/DKSPRCX9WB#1","type":"StatusList2021Entry","statusPurpose":"revocation","statusListIndex":1,"statusListCredential":"https://jchartrand.github.io/status-test-three/DKSPRCX9WB"},"proof":{"type":"Ed25519Signature2020","created":"2023-08-22T20:11:09Z","verificationMethod":"did:key:z6Mkf2rgv7ef8FmLJ5Py87LMa7nofQgv6AstdkgsXiiCUJEy#z6Mkf2rgv7ef8FmLJ5Py87LMa7nofQgv6AstdkgsXiiCUJEy","proofPurpose":"assertionMethod","proofValue":"z51uH32BFx2mNntaGE55MeHwespoAjetxDkTHBMKtbgGDdc5XiGSTaEGrRgANtT8DV5a6rTNnhT8FKRD4oVnhnxtG"}}, [ + 'X-Powered-By', + 'Express', + 'Access-Control-Allow-Origin', + '*', + 'Content-Type', + 'application/json; charset=utf-8', + 'Content-Length', + '1810', + 'ETag', + 'W/"712-fUBsd5PM46QPKrivsShMP8gvwtc"', + 'Date', + 'Tue, 22 Aug 2023 20:11:09 GMT', + 'Connection', + 'close' +]); */ +} + + /* export default () => {nock('http://localhost:4006', {"encodedQueryParams":true}) + .post('/instance/testing3/credentials/sign', {"@context":["https://www.w3.org/2018/credentials/v1","https://purl.imsglobal.org/spec/ob/v3p0/context.json","https://w3id.org/vc/status-list/2021/v1","https://w3id.org/security/suites/ed25519-2020/v1","https://w3id.org/vc/status-list/2021/v1"],"id":"urn:uuid:951b475e-b795-43bc-ba8f-a2d01efd2eb1","type":["VerifiableCredential","OpenBadgeCredential"],"issuer":{"id":"did:key:z6MkhVTX9BF3NGYX6cc7jWpbNnR7cAjH8LUffabZP8Qu4ysC","type":"Profile","name":"Dr David Malan","description":"Gordon McKay Professor of the Practice of Computer Science, Harvard University","url":"https://cs.harvard.edu/malan/","image":{"id":"https://certificates.cs50.io/static/success.jpg","type":"Image"}},"issuanceDate":"2020-01-01T00:00:00Z","name":"Introduction to Computer Science - CS50x","credentialSubject":{"type":"AchievementSubject","identifier":{"type":"IdentityObject","identityHash":"jc.chartrand@gmail.com","hashed":"false"},"achievement":{"id":"http://cs50.harvard.edu","type":"Achievement","criteria":{"narrative":"Completion of CS50X, including ten problem sets, ten labs, and one final project."},"description":"CS50 congratulates on completion of CS50x.","name":"Introduction to Computer Science - CS50x"}},"credentialStatus":{"id":"https://jchartrand.github.io/status-test-three/DKSPRCX9WB#1","type":"StatusList2021Entry","statusPurpose":"revocation","statusListIndex":1,"statusListCredential":"https://jchartrand.github.io/status-test-three/DKSPRCX9WB"}}) + .reply(200, {"@context":["https://www.w3.org/2018/credentials/v1","https://purl.imsglobal.org/spec/ob/v3p0/context.json","https://w3id.org/vc/status-list/2021/v1","https://w3id.org/security/suites/ed25519-2020/v1","https://w3id.org/vc/status-list/2021/v1"],"id":"urn:uuid:951b475e-b795-43bc-ba8f-a2d01efd2eb1","type":["VerifiableCredential","OpenBadgeCredential"],"issuer":{"id":"did:key:z6Mkuoj16AELhDkUk8tvTLA6e6yenGXSNoZ5urtprJoqhuww","type":"Profile","name":"Dr David Malan","description":"Gordon McKay Professor of the Practice of Computer Science, Harvard University","url":"https://cs.harvard.edu/malan/","image":{"id":"https://certificates.cs50.io/static/success.jpg","type":"Image"}},"issuanceDate":"2020-01-01T00:00:00Z","name":"Introduction to Computer Science - CS50x","credentialSubject":{"type":"AchievementSubject","identifier":{"type":"IdentityObject","identityHash":"jc.chartrand@gmail.com","hashed":"false"},"achievement":{"id":"http://cs50.harvard.edu","type":"Achievement","criteria":{"narrative":"Completion of CS50X, including ten problem sets, ten labs, and one final project."},"description":"CS50 congratulates on completion of CS50x.","name":"Introduction to Computer Science - CS50x"}},"credentialStatus":{"id":"https://jchartrand.github.io/status-test-three/DKSPRCX9WB#1","type":"StatusList2021Entry","statusPurpose":"revocation","statusListIndex":1,"statusListCredential":"https://jchartrand.github.io/status-test-three/DKSPRCX9WB"},"proof":{"type":"Ed25519Signature2020","created":"2023-08-03T17:27:29Z","verificationMethod":"did:key:z6Mkuoj16AELhDkUk8tvTLA6e6yenGXSNoZ5urtprJoqhuww#z6Mkuoj16AELhDkUk8tvTLA6e6yenGXSNoZ5urtprJoqhuww","proofPurpose":"assertionMethod","proofValue":"z53EF47PshAVsVtRBTBBv8A1vJvWptWn5p4QupVnAeZYWZJnTGAcABmAVYRZ4CR1xAjWyPrg7ktXerJ9PfUgSLfTh"}}, [ + 'X-Powered-By', + 'Express', + 'Access-Control-Allow-Origin', + '*', + 'Content-Type', + 'application/json; charset=utf-8', + 'Content-Length', + '1810', + 'ETag', + 'W/"712-0nL+TtiN38hiHrSNvQHR9Iqira4"', + 'Date', + 'Thu, 03 Aug 2023 17:27:29 GMT', + 'Connection', + 'keep-alive', + 'Keep-Alive', + 'timeout=5' +])} */ \ No newline at end of file diff --git a/src/test-fixtures/nocks/unprotected_status_update.js b/src/test-fixtures/nocks/unprotected_status_update.js new file mode 100644 index 0000000..a4a839c --- /dev/null +++ b/src/test-fixtures/nocks/unprotected_status_update.js @@ -0,0 +1,25 @@ +import nock from 'nock'; + +export default () => { + nock('http://localhost:4008', {"encodedQueryParams":true}) + .post('/credentials/status', {"credentialId":"urn:uuid:951b475e-b795-43bc-ba8f-a2d01efd2eb1","credentialStatus":[{"type":"StatusList2021Credential","status":"revoked"}]}) + .reply(200, "Credential status successfully updated", [ + 'X-Powered-By', + 'Express', + 'Access-Control-Allow-Origin', + '*', + 'Content-Type', + 'text/html; charset=utf-8', + 'Content-Length', + '38', + 'ETag', + 'W/"26-5PplkKqVB9H6fTLVTOo/weOUkgE"', + 'Date', + 'Wed, 23 Aug 2023 13:01:55 GMT', + 'Connection', + 'keep-alive', + 'Keep-Alive', + 'timeout=5' +]); + +}