From e22c132aad7c744d5fd4688808027734726f4551 Mon Sep 17 00:00:00 2001 From: Goi Jia Jian Date: Tue, 7 Jul 2020 02:03:33 +0800 Subject: [PATCH] fix: temporarily adding dist --- .gitignore | 2 +- dist/index.js | 43 ++++++ dist/ts/src/index.d.ts | 2 + dist/ts/src/util/logger.d.ts | 9 ++ dist/ts/src/verify.d.ts | 37 +++++ dist/ts/test/fixtures/v2/document.d.ts | 23 +++ dist/ts/test/fixtures/v3/document.d.ts | 6 + dist/ts/test/verify.v2.test.d.ts | 1 + dist/ts/test/verify.v3.test.d.ts | 1 + dist/util/logger.js | 32 +++++ dist/verify.js | 187 +++++++++++++++++++++++++ 11 files changed, 342 insertions(+), 1 deletion(-) create mode 100644 dist/index.js create mode 100644 dist/ts/src/index.d.ts create mode 100644 dist/ts/src/util/logger.d.ts create mode 100644 dist/ts/src/verify.d.ts create mode 100644 dist/ts/test/fixtures/v2/document.d.ts create mode 100644 dist/ts/test/fixtures/v3/document.d.ts create mode 100644 dist/ts/test/verify.v2.test.d.ts create mode 100644 dist/ts/test/verify.v3.test.d.ts create mode 100644 dist/util/logger.js create mode 100644 dist/verify.js diff --git a/.gitignore b/.gitignore index 267aee3..7604f62 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ .DS_Store /coverage -/dist +# /dist /node_modules *.log /.idea diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..4c508f0 --- /dev/null +++ b/dist/index.js @@ -0,0 +1,43 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +var _exportNames = { + verificationBuilder: true, + openAttestationVerifiers: true, + Verifier: true +}; +Object.defineProperty(exports, "verificationBuilder", { + enumerable: true, + get: function get() { + return _oaVerify.verificationBuilder; + } +}); +Object.defineProperty(exports, "openAttestationVerifiers", { + enumerable: true, + get: function get() { + return _oaVerify.openAttestationVerifiers; + } +}); +Object.defineProperty(exports, "Verifier", { + enumerable: true, + get: function get() { + return _oaVerify.Verifier; + } +}); + +var _oaVerify = require("@govtechsg/oa-verify"); + +var _verify = require("./verify"); + +Object.keys(_verify).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function get() { + return _verify[key]; + } + }); +}); \ No newline at end of file diff --git a/dist/ts/src/index.d.ts b/dist/ts/src/index.d.ts new file mode 100644 index 0000000..9c0de4e --- /dev/null +++ b/dist/ts/src/index.d.ts @@ -0,0 +1,2 @@ +export { verificationBuilder, openAttestationVerifiers, Verifier } from "@govtechsg/oa-verify"; +export * from "./verify"; diff --git a/dist/ts/src/util/logger.d.ts b/dist/ts/src/util/logger.d.ts new file mode 100644 index 0000000..3fdd413 --- /dev/null +++ b/dist/ts/src/util/logger.d.ts @@ -0,0 +1,9 @@ +import debug from "debug"; +export declare const trace: (namespace: string) => debug.Debugger; +export declare const info: (namespace: string) => debug.Debugger; +export declare const error: (namespace: string) => debug.Debugger; +export declare const getLogger: (namespace: string) => { + trace: debug.Debugger; + info: debug.Debugger; + error: debug.Debugger; +}; diff --git a/dist/ts/src/verify.d.ts b/dist/ts/src/verify.d.ts new file mode 100644 index 0000000..9569af8 --- /dev/null +++ b/dist/ts/src/verify.d.ts @@ -0,0 +1,37 @@ +import { Verifier, VerificationFragment, VerificationFragmentType, VerificationManagerOptions } from "@govtechsg/oa-verify"; +import { v2, v3, WrappedDocument } from "@govtechsg/open-attestation"; +export interface OpenCertsVerificationManagerOptions extends VerificationManagerOptions { + googleApiKey?: string; +} +export interface RegistryEntry { + name: string; + displayCard: boolean; + website?: string; + email?: string; + phone?: string; + logo?: string; + id?: string; +} +export interface Registry { + issuers: { + [key: string]: RegistryEntry; + }; +} +export interface GoogleSpreadsheetValues { + range: string; + majorDimension: string; + values: string[]; +} +export declare type OpencertsRegistryVerificationFragmentData = Partial & { + value: string; + status: "VALID" | "INVALID"; +}; +export declare const type = "ISSUER_IDENTITY"; +export declare const name = "OpencertsRegistryVerifier"; +export declare enum OpencertsRegistryCode { + INVALID_IDENTITY = 0, + SKIPPED = 1 +} +export declare const registryVerifier: Verifier | WrappedDocument, OpenCertsVerificationManagerOptions, OpencertsRegistryVerificationFragmentData | OpencertsRegistryVerificationFragmentData[]>; +export declare const isValid: (verificationFragments: VerificationFragment[], types?: VerificationFragmentType[]) => boolean; +export declare const verify: (document: WrappedDocument | WrappedDocument, options: OpenCertsVerificationManagerOptions) => Promise; diff --git a/dist/ts/test/fixtures/v2/document.d.ts b/dist/ts/test/fixtures/v2/document.d.ts new file mode 100644 index 0000000..3f41280 --- /dev/null +++ b/dist/ts/test/fixtures/v2/document.d.ts @@ -0,0 +1,23 @@ +import { v2, WrappedDocument } from "@govtechsg/open-attestation"; +interface CustomDocument extends v2.OpenAttestationDocument { + name: string; + issuedOn: string; + $template: string; + recipient: { + name: string; + }; +} +export declare const documentMainnetValidWithCertificateStore: WrappedDocument; +export declare const documentWithOneCertificateStoreIssuerInRegistry: WrappedDocument; +export declare const documentWithOneCertificateStoreIssuerNotInRegistry: WrappedDocument; +export declare const documentWithOneDocumentStoreIssuerInRegistryAndValidDnsTxt: WrappedDocument; +export declare const documentWithOneDocumentStoreIssuerNotInRegistryAndValidDnsTxt: WrappedDocument; +export declare const documentWithOneDocumentStoreIssuerInRegistryAndInvalidDnsTxt: WrappedDocument; +export declare const documentWithOneDocumentStoreIssuerNotInRegistryAndInvalidDnsTxt: WrappedDocument; +export declare const documentWithTwoCertificateStoreIssuerInRegistry: WrappedDocument; +export declare const documentWithTwoDocumentStoreIssuerInRegistryWithValidDnsTxt: WrappedDocument; +export declare const documentWithTwoDocumentStoreIssuerOneInRegistryWithValidDnsTxtAndSecondInvalid: WrappedDocument; +export declare const documentWithTwoDocumentStoreIssuerNotInRegistryWithoutValidDnsTxt: WrappedDocument; +export declare const documentWithTwoCertificateStoreIssuerWithOneInRegistry: WrappedDocument; +export declare const documentWithTwoCertificateStoreIssuerNotInRegistry: WrappedDocument; +export {}; diff --git a/dist/ts/test/fixtures/v3/document.d.ts b/dist/ts/test/fixtures/v3/document.d.ts new file mode 100644 index 0000000..af4cfa8 --- /dev/null +++ b/dist/ts/test/fixtures/v3/document.d.ts @@ -0,0 +1,6 @@ +import { v3, WrappedDocument } from "@govtechsg/open-attestation"; +export declare const documentWithDocumentStoreIssuerInRegistryAndValidDns: WrappedDocument; +export declare const documentWithDocumentStoreIssuerInRegistryAndInvalidDns: WrappedDocument; +export declare const documentWithDocumentStoreIssuerNotInRegistryAndValidDns: WrappedDocument; +export declare const documentWithDocumentStoreIssuerNotInRegistryAndInvalidDns: WrappedDocument; +export declare const documentRopstenValidWithDocumentStore: WrappedDocument; diff --git a/dist/ts/test/verify.v2.test.d.ts b/dist/ts/test/verify.v2.test.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/dist/ts/test/verify.v2.test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/dist/ts/test/verify.v3.test.d.ts b/dist/ts/test/verify.v3.test.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/dist/ts/test/verify.v3.test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/dist/util/logger.js b/dist/util/logger.js new file mode 100644 index 0000000..7ba3419 --- /dev/null +++ b/dist/util/logger.js @@ -0,0 +1,32 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getLogger = exports.error = exports.info = exports.trace = void 0; + +var _debug = _interopRequireDefault(require("debug")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const logger = (0, _debug.default)("@govtechsg/opencerts-verify"); + +const trace = namespace => logger.extend(`trace:${namespace}`); + +exports.trace = trace; + +const info = namespace => logger.extend(`info:${namespace}`); + +exports.info = info; + +const error = namespace => logger.extend(`error:${namespace}`); + +exports.error = error; + +const getLogger = namespace => ({ + trace: trace(namespace), + info: info(namespace), + error: error(namespace) +}); + +exports.getLogger = getLogger; \ No newline at end of file diff --git a/dist/verify.js b/dist/verify.js new file mode 100644 index 0000000..d2d755b --- /dev/null +++ b/dist/verify.js @@ -0,0 +1,187 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.verify = exports.isValid = exports.registryVerifier = exports.OpencertsRegistryCode = exports.name = exports.type = void 0; + +var _oaVerify = require("@govtechsg/oa-verify"); + +var _nodeFetch = _interopRequireDefault(require("node-fetch")); + +var _openAttestation = require("@govtechsg/open-attestation"); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } + +function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } + +function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } + +function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +const type = "ISSUER_IDENTITY"; +exports.type = type; +const name = "OpencertsRegistryVerifier"; // NEVER EVER REPLACE OR CHANGE A VALUE :) +// code for errors and invalid fragment + +exports.name = name; +let OpencertsRegistryCode; +exports.OpencertsRegistryCode = OpencertsRegistryCode; + +(function (OpencertsRegistryCode) { + OpencertsRegistryCode[OpencertsRegistryCode["INVALID_IDENTITY"] = 0] = "INVALID_IDENTITY"; + OpencertsRegistryCode[OpencertsRegistryCode["SKIPPED"] = 1] = "SKIPPED"; +})(OpencertsRegistryCode || (exports.OpencertsRegistryCode = OpencertsRegistryCode = {})); + +const storeToFragment = (registry, store) => { + const key = Object.keys(registry.issuers).find(k => k.toLowerCase() === store.toLowerCase()); + + if (key) { + return { + status: "VALID", + type, + name, + data: _objectSpread({ + status: "VALID", + value: store + }, registry.issuers[key]) + }; + } + + return { + status: "INVALID", + type, + name, + data: { + value: store, + status: "INVALID", + reason: { + code: OpencertsRegistryCode.INVALID_IDENTITY, + codeString: OpencertsRegistryCode[OpencertsRegistryCode.INVALID_IDENTITY], + message: `Document store ${store} not found in the registry` + } + }, + reason: { + code: OpencertsRegistryCode.INVALID_IDENTITY, + codeString: OpencertsRegistryCode[OpencertsRegistryCode.INVALID_IDENTITY], + message: `Document store ${store} not found in the registry` + } + }; +}; + +const registryVerifier = { + test: document => { + if ((0, _oaVerify.isWrappedV3Document)(document)) { + const documentData = (0, _openAttestation.getData)(document); + return documentData.proof.method === _openAttestation.v3.Method.DocumentStore; + } + + const documentData = (0, _openAttestation.getData)(document); + return documentData.issuers.some(issuer => "documentStore" in issuer || "certificateStore" in issuer); + }, + skip: () => { + return Promise.resolve({ + status: "SKIPPED", + type, + name, + reason: { + code: OpencertsRegistryCode.SKIPPED, + codeString: OpencertsRegistryCode[OpencertsRegistryCode.SKIPPED], + message: `Document issuers doesn't have "documentStore" or "certificateStore" property or ${_openAttestation.v3.Method.DocumentStore} method` + } + }); + }, + verify: function () { + var _verify = _asyncToGenerator(function* (document, options) { + var _issuerFragments$find; + + const apiKey = options.googleApiKey || process.env.GOOGLE_API_KEY; + const spreadsheetId = "1nhhD3XvHh2Ql_hW27LNw01fC-_I6Azt_XzYiYGhkmAU"; + const range = "Registry!A:H"; + const data = yield (0, _nodeFetch.default)(`https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${range}?valueRenderOption=UNFORMATTED_VALUE&key=${apiKey}`).then(res => res.json()); + const registry = { + issuers: {} + }; + data.values.forEach(row => { + // 0: documentStore, 1: name, 2: displayCard, 3: website, 4: email, 5: phone, 6: logo, 7: id, 8: group + registry.issuers[row[0]] = { + name: row[1], + displayCard: /true/i.test(row[2]) + }; + + if (row[2]) { + registry.issuers[row[0]] = _objectSpread({}, registry.issuers[row[0]], { + website: row[3], + email: row[4], + phone: row[5], + logo: row[6], + id: row[7] + }); + } + }); + + if ((0, _oaVerify.isWrappedV3Document)(document)) { + const documentData = (0, _openAttestation.getData)(document); + return storeToFragment(registry, documentData.proof.value); + } + + const documentData = (0, _openAttestation.getData)(document); + const issuerFragments = documentData.issuers.map(issuer => storeToFragment(registry, issuer.documentStore || issuer.certificateStore)); // if one issuer is valid => fragment status is valid otherwise if all issuers are invalid => invalid + + const status = issuerFragments.some(fragment => fragment.status === "VALID") ? "VALID" : "INVALID"; + return { + type, + name, + status, + data: issuerFragments.map(fragment => fragment.data), + reason: (_issuerFragments$find = issuerFragments.find(fragment => fragment.reason)) === null || _issuerFragments$find === void 0 ? void 0 : _issuerFragments$find.reason + }; + }); + + function verify(_x, _x2) { + return _verify.apply(this, arguments); + } + + return verify; + }() +}; +exports.registryVerifier = registryVerifier; + +const isValid = (verificationFragments, types = ["DOCUMENT_STATUS", "DOCUMENT_INTEGRITY", "ISSUER_IDENTITY"]) => { + if (verificationFragments.length < 1) { + throw new Error("Please provide at least one verification fragment to check"); + } + + if (types.length < 1) { + throw new Error("Please provide at least one type to check"); + } + + return types.every(currentType => { + var _fragmentForDnsVerifi, _fragmentForDnsVerifi2, _fragmentForDnsVerifi3; + + const verificationFragmentsForType = verificationFragments.filter(fragment => fragment.type === currentType); // return true if at least one fragment is valid + // and all fragments are valid or skipped + + const defaultCheck = verificationFragmentsForType.some(fragment => fragment.status === "VALID") && verificationFragmentsForType.every(fragment => fragment.status === "VALID" || fragment.status === "SKIPPED"); // return defaultCheck if it's true or if type is DOCUMENT_INTEGRITY or DOCUMENT_STATUS + + if (currentType === "DOCUMENT_STATUS" || currentType === "DOCUMENT_INTEGRITY" || defaultCheck) { + return defaultCheck; + } // if default check is false and type is issuer identity we need to perform further checks + + + const fragmentForDnsVerifier = verificationFragmentsForType.find(fragment => fragment.name === "OpenAttestationDnsTxt"); + const fragmentForRegistryVerifier = verificationFragmentsForType.find(fragment => fragment.name === name); + return (fragmentForRegistryVerifier === null || fragmentForRegistryVerifier === void 0 ? void 0 : fragmentForRegistryVerifier.status) === "VALID" || // if registry fragment is valid then issuer identity is valid + (fragmentForDnsVerifier === null || fragmentForDnsVerifier === void 0 ? void 0 : (_fragmentForDnsVerifi = fragmentForDnsVerifier.data) === null || _fragmentForDnsVerifi === void 0 ? void 0 : _fragmentForDnsVerifi.status) === "VALID" || ( // otherwise if there is one issuer and it's dns entry is valid then issuer identity is valid + fragmentForDnsVerifier === null || fragmentForDnsVerifier === void 0 ? void 0 : (_fragmentForDnsVerifi2 = fragmentForDnsVerifier.data) === null || _fragmentForDnsVerifi2 === void 0 ? void 0 : (_fragmentForDnsVerifi3 = _fragmentForDnsVerifi2.every) === null || _fragmentForDnsVerifi3 === void 0 ? void 0 : _fragmentForDnsVerifi3.call(_fragmentForDnsVerifi2, d => d.status === "VALID")) // otherwise if there are multiple issuers and all of them have valid dns entry then issuer identity is valid + ; + }); +}; + +exports.isValid = isValid; +const verify = (0, _oaVerify.verificationBuilder)([..._oaVerify.openAttestationVerifiers, registryVerifier]); +exports.verify = verify; \ No newline at end of file