diff --git a/.circleci/config.yml b/.circleci/config.yml index 632bf7c8..8879503f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -56,7 +56,7 @@ jobs: - restore_cache: keys: - v1-dependencies-{{ checksum "package.json" }}-{{checksum "package-lock.json"}} - - run: sudo npm install -g npm + - run: sudo npm install -g npm@6.14.13 - run: npm ci - save_cache: paths: diff --git a/__test__/lib/signerVerifier.test.js b/__test__/lib/signerVerifier.test.js index 7341eaee..729d6236 100644 --- a/__test__/lib/signerVerifier.test.js +++ b/__test__/lib/signerVerifier.test.js @@ -1,5 +1,4 @@ const nacl = require('tweetnacl'); -const { TextEncoder } = require('util'); const signerVerifier = require('../../src/lib/signerVerifier'); const { mockDids, @@ -8,8 +7,6 @@ const { keyPair, } = require('./util/did'); -const textEncoder = new TextEncoder(); - const DUMMY_MERKLE_ROOT = 'aa4149dda8fd2fac435898372f1de399140f6c50dbc3d40585c913701ce902c4'; describe('signerVerifier', () => { @@ -71,7 +68,7 @@ describe('signerVerifier', () => { const customSigner = { sign(proof) { - const encodedData = textEncoder.encode(proof.merkleRoot); + const encodedData = Buffer.from(proof.merkleRoot, 'hex'); const signature = nacl.sign.detached(encodedData, keypair.secretKey); diff --git a/audit-ci.json b/audit-ci.json index a3f2204d..ec5d9d63 100644 --- a/audit-ci.json +++ b/audit-ci.json @@ -2,5 +2,5 @@ "low": true, "package-manager": "auto", "report": true, - "allowlist": [1067342, 1067524, 1068287, 1068310, 1070273, 1070274] -} + "allowlist": [1086436, 1088664, 1088730, 1088811, 1088828, 1088831, 1088948, 1089128, 1089129, 1089152, 1089185, 1089394, 1089513, 1090135, 1090146, 1090169, 1090170] +} \ No newline at end of file diff --git a/dist/browser/AggregationHandler.js b/dist/browser/AggregationHandler.js index 8f6b879d..68af6652 100644 --- a/dist/browser/AggregationHandler.js +++ b/dist/browser/AggregationHandler.js @@ -1 +1 @@ -'use strict';var _toConsumableArray2=require("babel-runtime/helpers/toConsumableArray"),_toConsumableArray3=_interopRequireDefault(_toConsumableArray2);function _interopRequireDefault(a){return a&&a.__esModule?a:{default:a}}var _=require("lodash"),validateEmptyParametersOperators=function(a){if(!_.isEmpty(a))throw new Error("parameters should be empty");return!0},validateNotEmptyParametersOperators=function(a){if(!a&&_.isEmpty(a))throw new Error("parameters should not be empty");return!0},validatePathParametersOperators=function(a){if(!_.isString(a))throw new Error("parameters should be string");return!0},validateNumberParametersOperators=function(a){if(!_.isNumber(a))throw new Error("parameters should be number");return!0},validateObjectParametersOperators=function(a){if(!_.isObject(a))throw new Error("parameters should be object");return!0},sort=function(a,b){var c=_.keys(b)[0],d=b[c],e=_.sortBy(a,c);return"ASC"===d?e:_.reverse(e)},AGGREGATION_OPERATORS_MAP={none:function(a,b){return validateEmptyParametersOperators(b)?[].concat((0,_toConsumableArray3.default)(a)):null},$limit:function(a,b){return validateNumberParametersOperators(b)?[].concat((0,_toConsumableArray3.default)(_.slice(a,0,b))):null},$min:function(a,b){return validatePathParametersOperators(b)?[_.minBy(a,b)]:null},$max:function(a,b){return validatePathParametersOperators(b)?[_.maxBy(a,b)]:null},$first:function(a,b){return validateNotEmptyParametersOperators(b)?[_.first(a)]:null},$last:function(a,b){return validateNotEmptyParametersOperators(b)?[_.last(a)]:null},$sort:function(a,b){return validateObjectParametersOperators(b)?[].concat((0,_toConsumableArray3.default)(sort(a,b))):null}};function aggregate(a,b){var c=[].concat((0,_toConsumableArray3.default)(a));return _.forEach(b,function(a){var b=_.keys(a)[0];if(!_.includes(_.keys(AGGREGATION_OPERATORS_MAP),b))throw new Error("Invalid operator: "+b);var d=a[b],e=AGGREGATION_OPERATORS_MAP[b];c=e(c,d)}),c}module.exports=aggregate; \ No newline at end of file +"use strict";var _interopRequireDefault=require("@babel/runtime/helpers/interopRequireDefault"),_toConsumableArray2=_interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")),_=require("lodash"),validateEmptyParametersOperators=function(a){if(!_.isEmpty(a))throw new Error("parameters should be empty");return!0},validateNotEmptyParametersOperators=function(a){if(!a&&_.isEmpty(a))throw new Error("parameters should not be empty");return!0},validatePathParametersOperators=function(a){if(!_.isString(a))throw new Error("parameters should be string");return!0},validateNumberParametersOperators=function(a){if(!_.isNumber(a))throw new Error("parameters should be number");return!0},validateObjectParametersOperators=function(a){if(!_.isObject(a))throw new Error("parameters should be object");return!0},sort=function(a,b){var c=_.keys(b)[0],d=b[c],e=_.sortBy(a,c);return"ASC"===d?e:_.reverse(e)},AGGREGATION_OPERATORS_MAP={none:function(a,b){return validateEmptyParametersOperators(b)?(0,_toConsumableArray2.default)(a):null},$limit:function(a,b){return validateNumberParametersOperators(b)?(0,_toConsumableArray2.default)(_.slice(a,0,b)):null},$min:function(a,b){return validatePathParametersOperators(b)?[_.minBy(a,b)]:null},$max:function(a,b){return validatePathParametersOperators(b)?[_.maxBy(a,b)]:null},$first:function(a,b){return validateNotEmptyParametersOperators(b)?[_.first(a)]:null},$last:function(a,b){return validateNotEmptyParametersOperators(b)?[_.last(a)]:null},$sort:function(a,b){return validateObjectParametersOperators(b)?(0,_toConsumableArray2.default)(sort(a,b)):null}};function aggregate(a,b){var c=(0,_toConsumableArray2.default)(a);return _.forEach(b,function(a){var b=_.keys(a)[0];if(!_.includes(_.keys(AGGREGATION_OPERATORS_MAP),b))throw new Error("Invalid operator: ".concat(b));var d=a[b],e=AGGREGATION_OPERATORS_MAP[b];c=e(c,d)}),c}module.exports=aggregate; \ No newline at end of file diff --git a/dist/browser/SecureRandom.js b/dist/browser/SecureRandom.js index 6f366569..0da5fa8a 100644 --- a/dist/browser/SecureRandom.js +++ b/dist/browser/SecureRandom.js @@ -1 +1 @@ -'use strict';var _classCallCheck2=require("babel-runtime/helpers/classCallCheck"),_classCallCheck3=_interopRequireDefault(_classCallCheck2),_createClass2=require("babel-runtime/helpers/createClass"),_createClass3=_interopRequireDefault(_createClass2);function _interopRequireDefault(a){return a&&a.__esModule?a:{default:a}}var sjcl=require("sjcl"),logger=require("./logger"),SecureRandom=function(){function a(b){if((0,_classCallCheck3.default)(this,a),logger.debug("Init Secure Random"),this.sjclRandom=new sjcl.prng(10),b){var c=sjcl.codec.hex.toBits(b);this.sjclRandom.addEntropy(c,void 0,"csprng"),this.isSeeded=!0}else try{logger.debug("Trying crypto");var d=require("crypto").randomBytes(1024).toString("hex"),e=sjcl.codec.hex.toBits(d);this.sjclRandom.addEntropy(e,void 0,"csprng"),this.isSeeded=!0}catch(a){logger.warn("Crypto: "+a),this.isSeeded=!1}}return(0,_createClass3.default)(a,[{key:"wordWith",value:function(a){if(!this.isSeeded)throw new Error("Can't user SecureRandom before seeding");var b=this.sjclRandom.randomWords(a/8,10);return sjcl.codec.hex.fromBits(b)}}]),a}();module.exports=SecureRandom; \ No newline at end of file +"use strict";var _interopRequireDefault=require("@babel/runtime/helpers/interopRequireDefault"),_classCallCheck2=_interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")),_createClass2=_interopRequireDefault(require("@babel/runtime/helpers/createClass")),sjcl=require("sjcl"),logger=require("./logger"),SecureRandom=function(){function a(b){if((0,_classCallCheck2.default)(this,a),logger.debug("Init Secure Random"),this.sjclRandom=new sjcl.prng(10),b){var c=sjcl.codec.hex.toBits(b);this.sjclRandom.addEntropy(c,void 0,"csprng"),this.isSeeded=!0}else try{logger.debug("Trying crypto");var d=require("crypto").randomBytes(1024).toString("hex"),e=sjcl.codec.hex.toBits(d);this.sjclRandom.addEntropy(e,void 0,"csprng"),this.isSeeded=!0}catch(a){logger.warn("Crypto: ".concat(a)),this.isSeeded=!1}}return(0,_createClass2.default)(a,[{key:"wordWith",value:function(a){if(!this.isSeeded)throw new Error("Can't user SecureRandom before seeding");var b=this.sjclRandom.randomWords(a/8,10);return sjcl.codec.hex.fromBits(b)}}]),a}();module.exports=SecureRandom; \ No newline at end of file diff --git a/dist/browser/claim/Claim.js b/dist/browser/claim/Claim.js index 7d6c9d8d..0437f0e6 100644 --- a/dist/browser/claim/Claim.js +++ b/dist/browser/claim/Claim.js @@ -1 +1 @@ -'use strict';var _toConsumableArray2=require("babel-runtime/helpers/toConsumableArray"),_toConsumableArray3=_interopRequireDefault(_toConsumableArray2),_getIterator2=require("babel-runtime/core-js/get-iterator"),_getIterator3=_interopRequireDefault(_getIterator2),_getPrototypeOf=require("babel-runtime/core-js/object/get-prototype-of"),_getPrototypeOf2=_interopRequireDefault(_getPrototypeOf),_classCallCheck2=require("babel-runtime/helpers/classCallCheck"),_classCallCheck3=_interopRequireDefault(_classCallCheck2),_createClass2=require("babel-runtime/helpers/createClass"),_createClass3=_interopRequireDefault(_createClass2),_possibleConstructorReturn2=require("babel-runtime/helpers/possibleConstructorReturn"),_possibleConstructorReturn3=_interopRequireDefault(_possibleConstructorReturn2),_get2=require("babel-runtime/helpers/get"),_get3=_interopRequireDefault(_get2),_inherits2=require("babel-runtime/helpers/inherits"),_inherits3=_interopRequireDefault(_inherits2);function _interopRequireDefault(a){return a&&a.__esModule?a:{default:a}}var _=require("lodash"),sjcl=require("sjcl"),_require=require("@identity.com/uca"),UserCollectableAttribute=_require.UserCollectableAttribute,definitions=require("./definitions"),_require2=require("../services"),services=_require2.services,validIdentifiers=_.map(definitions,function(a){return a.identifier}),getDefinition=function(a,b){return b?_.find(definitions,{identifier:a,version:b}):_.find(definitions,{identifier:a})},isArrayAttestableValue=function(a){return-1=a.length?{done:!0}:{done:!1,value:a[d++]}},e:function(a){throw a},f:e}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var f,g=!0,h=!1;return{s:function(){c=c.call(a)},n:function(){var a=c.next();return g=a.done,a},e:function(a){h=!0,f=a},f:function(){try{g||null==c.return||c.return()}finally{if(h)throw f}}}}function _unsupportedIterableToArray(a,b){if(a){if("string"==typeof a)return _arrayLikeToArray(a,b);var c=Object.prototype.toString.call(a).slice(8,-1);return"Object"===c&&a.constructor&&(c=a.constructor.name),"Map"===c||"Set"===c?Array.from(a):"Arguments"===c||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(c)?_arrayLikeToArray(a,b):void 0}}function _arrayLikeToArray(a,b){(null==b||b>a.length)&&(b=a.length);for(var c=0,d=Array(b);ct.getTime()&&k.push(b)}}return _.isEmpty(j)&&_.isEmpty(l)&&_.isEmpty(m)&&_.isEmpty(n)&&_.isEmpty(k)&&(f=!0),f}function requesterGrantVerify(a,b,c,d){var e=_.get(a.proof,"anchor.subject.label"),f=_.get(a.proof,"anchor.subject.pub"),g=_.get(a.proof,"anchor.subject.data");if(_.isEmpty(a.granted)||_.isEmpty(e)||_.isEmpty(f))return!1;var h=sha256(""+e+g+b+c),i=services.container.CryptoManager,j=d;if(_.isEmpty(j)){if(!_.isFunction(i.installKey))throw new Error("CryptoManager does not support installKey, please use a `keyName` instead.");j="TEMP_KEY_NAME_"+new Date().getTime(),i.installKey(j,f)}return i.verify(j,h,a.granted)}function transformDate(a){return new Date(a.year,a.month-1,a.day).getTime()/1e3}var VERIFY_LEVELS={INVALID:-1,PROOFS:0,ANCHOR:1,GRANTED:2,BLOCKCHAIN:3};function verifyRequiredClaims(a,b){if(!_.isEmpty(a.required)){var c=b.map(function(a){return a.identifier}),d=_.difference(a.required,c);if(!_.isEmpty(d))throw new Error("Missing required claim(s): "+_.join(d,", "))}}function verifyRequiredClaimsFromJSON(a,b){var c=_.get(b,"proof.leaves");if(!_.isEmpty(a.required)&&c){var d=c.map(function(a){return a.identifier}),e=_.difference(a.required,d);if(!_.isEmpty(e))throw new Error("Missing required claim(s): "+_.join(e,", "))}}function getCredentialDefinition(a,b){var c;if(c=b?_.find(definitions,{identifier:a,version:""+b}):_.find(definitions,{identifier:a}),!c)throw new Error("Credential definition for "+a+" v"+b+" not found");return c}function VerifiableCredentialBaseConstructor(a,b,c,d,e,f){var g=this;this.id=uuidv4(),this.issuer=b;var h=new Claim("cvc:Meta:issuer",this.issuer);this.issuanceDate=new Date().toISOString();var i=new Claim("cvc:Meta:issuanceDate",this.issuanceDate);this.identifier=a,this.expirationDate=c?timestamp.toDate(timestamp.now(c)).toISOString():null;var j=new Claim("cvc:Meta:expirationDate",this.expirationDate?this.expirationDate:"null"),k=j?_.concat(d,h,i,j):_.concat(d,h,i);if(!_.includes(validIdentifiers(),a))throw new Error(a+" is not defined");var l=getCredentialDefinition(a,e);if(this.version=""+e||l.version,this.type=["Credential",a],this.transient=l.transient||!1,f&&(this.evidence=serializeEvidence(f)),!_.isEmpty(d)){if(verifyRequiredClaims(l,d),this.claim=new ClaimModel(d),this.proof=new CvcMerkleProof(k),!_.isEmpty(l.excludes)){var m=_.remove(this.proof.leaves,function(a){return _.includes(l.excludes,a.identifier)});_.forEach(m,function(a){_.unset(g.claim,a.claimPath)})}this.granted=null}this.getGlobalIdentifier=function(){return"credential-"+g.identifier+"-"+g.version},this.filter=function(a){var b=_.cloneDeep(g);return _.remove(b.proof.leaves,function(b){return!_.includes(a,b.identifier)}),b.claim={},_.forEach(b.proof.leaves,function(a){_.set(b.claim,a.claimPath,_.get(g.claim,a.claimPath))}),b},this.requestAnchor=function(){var a=(0,_asyncToGenerator3.default)(_regenerator2.default.mark(function a(b){var c,d,e;return _regenerator2.default.wrap(function(a){for(;;)switch(a.prev=a.next){case 0:if(!g.transient){a.next=3;break}return g.proof.anchor={type:"transient",subject:{label:g.identifier,data:g.proof.merkleRoot}},a.abrupt("return",g);case 3:return c=services.container.AnchorService,d=_.merge({},b,{subject:{label:g.identifier,data:g.proof.merkleRoot}}),a.next=7,c.anchor(d);case 7:return e=a.sent,g.proof.anchor=e,a.abrupt("return",g);case 10:case"end":return a.stop();}},a,g)}));return function(){return a.apply(this,arguments)}}(),this.updateAnchor=(0,_asyncToGenerator3.default)(_regenerator2.default.mark(function a(){var b,c;return _regenerator2.default.wrap(function(a){for(;;)switch(a.prev=a.next){case 0:if(!g.transient){a.next=3;break}return g.proof.anchor={type:"transient",subject:{label:g.identifier,data:g.proof.merkleRoot}},a.abrupt("return",g);case 3:return b=services.container.AnchorService,a.next=6,b.update(g.proof.anchor);case 6:return c=a.sent,g.proof.anchor=c,a.abrupt("return",g);case 9:case"end":return a.stop();}},a,g)})),this.verifyProofs=function(){return nonCryptographicallySecureVerify(g)},this.verify=function(a,b){var c=b||{},d=c.requestorId,e=c.requestId,f=c.keyName,h=_.isNil(a)?VERIFY_LEVELS.GRANTED:a,i=VERIFY_LEVELS.INVALID;return i===VERIFY_LEVELS.INVALID&&h>=VERIFY_LEVELS.PROOFS&&g.verifyProofs()&&(i=VERIFY_LEVELS.PROOFS),i===VERIFY_LEVELS.PROOFS&&h>=VERIFY_LEVELS.ANCHOR&&g.verifyAttestation()&&(i=VERIFY_LEVELS.ANCHOR),i===VERIFY_LEVELS.ANCHOR&&h>=VERIFY_LEVELS.GRANTED&&g.verifyGrant(d,e,f)&&(i=VERIFY_LEVELS.GRANTED),i},this.verifySignature=function(a){return"transient"===g.proof.anchor.type||services.container.AnchorService.verifySignature(g.proof,a)},this.verifyAttestation=(0,_asyncToGenerator3.default)(_regenerator2.default.mark(function a(){return _regenerator2.default.wrap(function(a){for(;;)switch(a.prev=a.next){case 0:if("transient"!==g.proof.anchor.type&&"dummynet"!==g.proof.anchor.network){a.next=2;break}return a.abrupt("return",!0);case 2:return a.abrupt("return",services.container.AnchorService.verifyAttestation(g.proof));case 3:case"end":return a.stop();}},a,g)})),this.revokeAttestation=(0,_asyncToGenerator3.default)(_regenerator2.default.mark(function a(){return _regenerator2.default.wrap(function(a){for(;;)switch(a.prev=a.next){case 0:if("transient"!==g.proof.type){a.next=2;break}return a.abrupt("return");case 2:return a.abrupt("return",services.container.AnchorService.revokeAttestation(g.proof));case 3:case"end":return a.stop();}},a,g)})),this.isRevoked=(0,_asyncToGenerator3.default)(_regenerator2.default.mark(function a(){return _regenerator2.default.wrap(function(a){for(;;)switch(a.prev=a.next){case 0:if("transient"!==g.proof.type){a.next=2;break}return a.abrupt("return",!1);case 2:return a.abrupt("return",services.container.AnchorService.isRevoked(g.proof));case 3:case"end":return a.stop();}},a,g)}));var n=function(a){return _.isString(a)?convertDeltaToTimestamp(a):a};return this.isMatch=function(a){var b=_.cloneDeep(g.claim),c=transformConstraint(a),d=function(a){var c=_.keys(a)[0],d=_.get(b,c);return isDateStructure(d)&&(_.set(b,c,transformDate(d)),_.set(a,c,_.mapValues(a[c],n))),sift(a)([b])};return c.reduce(function(a,b){return a&&d(b)},!0)},this.grantUsageFor=function(a,b,c){var d=c.keyName,e=c.pvtKey;if(_.isEmpty(_.get(g.proof,"anchor.subject.label"))||_.isEmpty(_.get(g.proof,"anchor.subject.data")))throw new Error("Invalid credential attestation/anchor");if(!g.verifySignature())throw new Error("Invalid credential attestation/anchor signature");if(!a||!b||!(d||e))throw new Error("Missing required parameter: requestorId, requestId or key");var f=""+g.proof.anchor.subject.label+g.proof.anchor.subject.data+a+b,h=sha256(f),i=services.container.CryptoManager,j=d;if(e){if(!_.isFunction(i.installKey))throw new Error("You provide a `pvtKey` but the CryptoManager does not support it, use a `keyName` instead.");j="TEMP_KEY_NAME_"+new Date().getTime(),i.installKey(j,e)}var k=i.sign(j,h);g.granted=k},this.verifyGrant=function(a,b,c){return requesterGrantVerify(g,a,b,c)},this}var CREDENTIAL_META_FIELDS=["id","identifier","issuer","issuanceDate","expirationDate","version","type"],getCredentialMeta=function(a){return _.pick(a,CREDENTIAL_META_FIELDS)};function transformMetaConstraint(a){var b=[],c=_.keys(a.meta);return _.forEach(c,function(c){var d=a.meta[c],e={};if("credential"===c)e.identifier=d;else if(d.is)e[c]=d.is;else throw new Error("Malformed meta constraint \""+c+"\": missing the IS");b.push(e)}),b}var isMatchCredentialMeta=function(a,b){var c=transformMetaConstraint(b);if(_.isEmpty(c))return!1;var d=function(b){return sift(b)([a])};return c.reduce(function(a,b){return a&&d(b)},!0)};VerifiableCredentialBaseConstructor.CREDENTIAL_META_FIELDS=CREDENTIAL_META_FIELDS,VerifiableCredentialBaseConstructor.getCredentialMeta=getCredentialMeta,VerifiableCredentialBaseConstructor.isMatchCredentialMeta=isMatchCredentialMeta,VerifiableCredentialBaseConstructor.fromJSON=function(a){var b=getCredentialDefinition(a.identifier,a.version);verifyRequiredClaimsFromJSON(b,a);var c=new VerifiableCredentialBaseConstructor(a.identifier,a.issuer);return c.id=_.clone(a.id),c.issuanceDate=_.clone(a.issuanceDate),c.expirationDate=_.clone(a.expirationDate),c.identifier=_.clone(a.identifier),c.version=_.clone(a.version),c.type=_.cloneDeep(a.type),c.claim=_.cloneDeep(a.claim),c.proof=_.cloneDeep(a.proof),c.granted=_.clone(a.granted)||null,c},VerifiableCredentialBaseConstructor.getAllProperties=function(a){var b=_.find(definitions,{identifier:a});if(b){var c=[];_.forEach(b.depends,function(a){c.push.apply(c,(0,_toConsumableArray3.default)(Claim.getAllProperties(a)))});var d=[];return _.forEach(b.excludes,function(a){d.push.apply(d,(0,_toConsumableArray3.default)(Claim.getAllProperties(a)))}),_.difference(c,d)}return null},VerifiableCredentialBaseConstructor.VERIFY_LEVELS=VERIFY_LEVELS,VerifiableCredentialBaseConstructor.nonCryptographicallySecureVerify=nonCryptographicallySecureVerify,VerifiableCredentialBaseConstructor.cryptographicallySecureVerify=cryptographicallySecureVerify,VerifiableCredentialBaseConstructor.requesterGrantVerify=requesterGrantVerify,module.exports=VerifiableCredentialBaseConstructor; \ No newline at end of file +"use strict";var _interopRequireDefault=require("@babel/runtime/helpers/interopRequireDefault"),_regenerator=_interopRequireDefault(require("@babel/runtime/regenerator")),_toConsumableArray2=_interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")),_defineProperty2=_interopRequireDefault(require("@babel/runtime/helpers/defineProperty")),_asyncToGenerator2=_interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));function ownKeys(a,b){var c=Object.keys(a);if(Object.getOwnPropertySymbols){var d=Object.getOwnPropertySymbols(a);b&&(d=d.filter(function(b){return Object.getOwnPropertyDescriptor(a,b).enumerable})),c.push.apply(c,d)}return c}function _objectSpread(a){for(var b,c=1;cv.getTime()&&l.push(c))),_.isEmpty(k)&&_.isEmpty(m)&&_.isEmpty(n)&&_.isEmpty(o)&&_.isEmpty(l)&&(g=!0),b.abrupt("return",g);case 22:case"end":return b.stop();}},b)})),_nonCryptographicallySecureVerify.apply(this,arguments)}function cryptographicallySecureVerify(){return _cryptographicallySecureVerify.apply(this,arguments)}function _cryptographicallySecureVerify(){return _cryptographicallySecureVerify=(0,_asyncToGenerator2.default)(_regenerator.default.mark(function d(a,b,c){var e,f,g;return _regenerator.default.wrap(function(d){for(;;)switch(d.prev=d.next){case 0:return d.next=2,nonCryptographicallySecureVerify(a);case 2:if(e=d.sent,e){d.next=5;break}return d.abrupt("return",!1);case 5:if(!b){d.next=11;break}return d.next=8,b(a.proof);case 8:if(f=d.sent,f){d.next=11;break}return d.abrupt("return",!1);case 11:if(!c){d.next=17;break}return d.next=14,c(a.proof);case 14:if(g=d.sent,g){d.next=17;break}return d.abrupt("return",!1);case 17:return d.abrupt("return",!0);case 18:case"end":return d.stop();}},d)})),_cryptographicallySecureVerify.apply(this,arguments)}function requesterGrantVerify(){return _requesterGrantVerify.apply(this,arguments)}function _requesterGrantVerify(){return _requesterGrantVerify=(0,_asyncToGenerator2.default)(_regenerator.default.mark(function e(a,b,c,d){var f,g,h,i,j,k,l;return _regenerator.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(f=_.get(a.proof,"anchor.subject.label"),g=_.get(a.proof,"anchor.subject.pub"),h=_.get(a.proof,"anchor.subject.data"),!(_.isEmpty(a.proof.granted)||_.isEmpty(f)||_.isEmpty(g))){e.next=5;break}return e.abrupt("return",!1);case 5:if(i="".concat(f).concat(h).concat(b).concat(c),j=sha256(i),k=services.container.CryptoManager,l=d,!_.isEmpty(l)){e.next=14;break}if(_.isFunction(k.installKey)){e.next=12;break}throw new Error("CryptoManager does not support installKey, please use a `keyName` instead.");case 12:l="TEMP_KEY_NAME_".concat(new Date().getTime()),k.installKey(l,g);case 14:return e.abrupt("return",k.verify(l,j,a.proof.granted));case 15:case"end":return e.stop();}},e)})),_requesterGrantVerify.apply(this,arguments)}function transformDate(a){return new Date(a.year,a.month-1,a.day).getTime()/1e3}var VERIFY_LEVELS={INVALID:-1,PROOFS:0,ANCHOR:1,GRANTED:2,BLOCKCHAIN:3};function verifyRequiredClaims(a,b){if(!_.isEmpty(a.required)){var c=b.map(function(a){return a.identifier}),d=_.difference(a.required,c);if(!_.isEmpty(d))throw new Error("Missing required claim(s): ".concat(_.join(d,", ")))}}function getCredentialDefinition(a,b){var c=_.find(definitions,{identifier:a});if(!c)throw new Error("Credential definition for ".concat(a," v").concat(b," not found"));return c}function VerifiableCredentialBaseConstructor(a,b,c,d,e,f,g){var h=this,i=parseIdentifier(a),j=i?i[4]:"1";this.id=uuidv4(),this.issuer=b;var k=new Claim("cvc:Meta:issuer",this.issuer);this.issuanceDate=new Date().toISOString();var l=new Claim("cvc:Meta:issuanceDate",this.issuanceDate);this.identifier=a,this.expirationDate=c?timestamp.toDate(timestamp.now(c)).toISOString():null;var m=new Claim("cvc:Meta:expirationDate",this.expirationDate?this.expirationDate:"null"),n=m?_.concat(e,k,l,m):_.concat(e,k,l);if(!_.includes(validIdentifiers(),a))throw new Error("".concat(a," is not defined"));var o=getCredentialDefinition(a,j);if(this.type=["VerifiableCredential","IdentityCredential"],this.transient=o.transient||!1,f&&(this.evidence=serializeEvidence(f)),this.credentialSubject={id:d},!_.isEmpty(e)&&(verifyRequiredClaims(o,e),this.credentialSubject=_objectSpread(_objectSpread({},this.credentialSubject),new ClaimModel(e)),this.proof=new CvcMerkleProof(n,g?g.signer:null),!_.isEmpty(o.excludes))){var p=_.remove(this.proof.leaves,function(a){return _.includes(o.excludes,a.identifier)});_.forEach(p,function(a){_.unset(h.credentialSubject,a.claimPath)})}this.getGlobalIdentifier=function(){return"credential-".concat(h.identifier,"-").concat(j)},this.filter=function(a){var b=_.cloneDeep(h);return _.remove(b.proof.leaves,function(b){return!_.includes(a,b.identifier)}),b.credentialSubject={},_.forEach(b.proof.leaves,function(a){_.set(b.credentialSubject,a.claimPath,_.get(h.credentialSubject,a.claimPath))}),b},this.requestAnchor=function(){var a=(0,_asyncToGenerator2.default)(_regenerator.default.mark(function b(a){var c,d,e;return _regenerator.default.wrap(function(b){for(;;)switch(b.prev=b.next){case 0:if(!h.transient){b.next=3;break}return h.proof.anchor={type:"transient",subject:{label:h.identifier,data:h.proof.merkleRoot}},b.abrupt("return",h);case 3:return c=services.container.AnchorService,d=_.merge({},a,{subject:{label:h.identifier,data:h.proof.merkleRoot}}),b.next=7,c.anchor(d);case 7:return e=b.sent,h.proof.anchor=e,b.abrupt("return",h);case 10:case"end":return b.stop();}},b)}));return function(){return a.apply(this,arguments)}}(),this.updateAnchor=(0,_asyncToGenerator2.default)(_regenerator.default.mark(function a(){var b,c;return _regenerator.default.wrap(function(a){for(;;)switch(a.prev=a.next){case 0:if(!h.transient){a.next=3;break}return h.proof.anchor={type:"transient",subject:{label:h.identifier,data:h.proof.merkleRoot}},a.abrupt("return",h);case 3:return b=services.container.AnchorService,a.next=6,b.update(h.proof.anchor);case 6:return c=a.sent,h.proof.anchor=c,a.abrupt("return",h);case 9:case"end":return a.stop();}},a)})),this.verifyProofs=function(){return nonCryptographicallySecureVerify(h)},this.verify=function(){var a=(0,_asyncToGenerator2.default)(_regenerator.default.mark(function c(a,b){var d,e,f,g,i,j;return _regenerator.default.wrap(function(c){for(;;)switch(c.prev=c.next){case 0:if(d=b||{},e=d.requestorId,f=d.requestId,g=d.keyName,i=_.isNil(a)?VERIFY_LEVELS.GRANTED:a,j=VERIFY_LEVELS.INVALID,c.t0=j===VERIFY_LEVELS.INVALID&&i>=VERIFY_LEVELS.PROOFS,!c.t0){c.next=8;break}return c.next=7,h.verifyProofs();case 7:c.t0=c.sent;case 8:if(!c.t0){c.next=10;break}j=VERIFY_LEVELS.PROOFS;case 10:return j===VERIFY_LEVELS.PROOFS&&i>=VERIFY_LEVELS.ANCHOR&&h.verifyAttestation()&&(j=VERIFY_LEVELS.ANCHOR),j===VERIFY_LEVELS.ANCHOR&&i>=VERIFY_LEVELS.GRANTED&&h.verifyGrant(e,f,g)&&(j=VERIFY_LEVELS.GRANTED),c.abrupt("return",j);case 13:case"end":return c.stop();}},c)}));return function(){return a.apply(this,arguments)}}(),this.verifyAnchorSignature=function(a){return"transient"===h.proof.anchor.type||services.container.AnchorService.verifySignature(h.proof,a)},this.verifyMerkletreeSignature=(0,_asyncToGenerator2.default)(_regenerator.default.mark(function a(){var b;return _regenerator.default.wrap(function(a){for(;;)switch(a.prev=a.next){case 0:return a.next=2,signerVerifier.verifier(h.issuer,h.proof.merkleRootSignature.verificationMethod);case 2:return b=a.sent,a.abrupt("return",b.verify(h));case 4:case"end":return a.stop();}},a)})),this.verifyAttestation=(0,_asyncToGenerator2.default)(_regenerator.default.mark(function a(){return _regenerator.default.wrap(function(a){for(;;)switch(a.prev=a.next){case 0:if("transient"!==h.proof.anchor.type&&"dummynet"!==h.proof.anchor.network){a.next=2;break}return a.abrupt("return",!0);case 2:return a.abrupt("return",services.container.AnchorService.verifyAttestation(h.proof));case 3:case"end":return a.stop();}},a)})),this.revokeAttestation=(0,_asyncToGenerator2.default)(_regenerator.default.mark(function a(){return _regenerator.default.wrap(function(a){for(;;)switch(a.prev=a.next){case 0:if("transient"!==h.proof.type){a.next=2;break}return a.abrupt("return");case 2:return a.abrupt("return",services.container.AnchorService.revokeAttestation(h.proof));case 3:case"end":return a.stop();}},a)})),this.isRevoked=(0,_asyncToGenerator2.default)(_regenerator.default.mark(function a(){return _regenerator.default.wrap(function(a){for(;;)switch(a.prev=a.next){case 0:if("transient"!==h.proof.type){a.next=2;break}return a.abrupt("return",!1);case 2:return a.abrupt("return",services.container.AnchorService.isRevoked(h.proof));case 3:case"end":return a.stop();}},a)}));var q=function(a){return _.isString(a)?convertDeltaToTimestamp(a):a};return this.isMatch=function(a){var b=_.cloneDeep(h.credentialSubject),c=transformConstraint(a),d=function(a){var c=_.keys(a)[0],d=_.get(b,c);return isDateStructure(d)&&(_.set(b,c,transformDate(d)),_.set(a,c,_.mapValues(a[c],q))),sift(a)([b])};return c.reduce(function(a,b){return a&&d(b)},!0)},this.grantUsageFor=function(a,b,c){var d=c.keyName,e=c.pvtKey;if(_.isEmpty(_.get(h.proof,"anchor.subject.label"))||_.isEmpty(_.get(h.proof,"anchor.subject.data")))throw new Error("Invalid credential attestation/anchor");if(!h.verifyAnchorSignature())throw new Error("Invalid credential attestation/anchor signature");if(!a||!b||!(d||e))throw new Error("Missing required parameter: requestorId, requestId or key");var f="".concat(h.proof.anchor.subject.label).concat(h.proof.anchor.subject.data).concat(a).concat(b),g=sha256(f),i=services.container.CryptoManager,j=d;if(e){if(!_.isFunction(i.installKey))throw new Error("You provide a `pvtKey` but the CryptoManager does not support it, use a `keyName` instead.");j="TEMP_KEY_NAME_".concat(new Date().getTime()),i.installKey(j,e)}var k=i.sign(j,g);h.proof.granted=k},this.toJSON=function(){var a=_.pick(h,["id","identifier","issuer","issuanceDate","expirationDate","type","credentialSubject","proof"]);for(var b in a)(null===a[b]||void 0===a[b])&&delete a[b];return _objectSpread({"@context":["https://www.w3.org/2018/credentials/v1","https://www.identity.com/credentials/v".concat(j)]},a)},this.verifyGrant=function(a,b,c){return requesterGrantVerify(h,a,b,c)},this}var CREDENTIAL_META_FIELDS=["id","identifier","issuer","issuanceDate","expirationDate","type"],getCredentialMeta=function(a){return _.pick(a,CREDENTIAL_META_FIELDS)};function transformMetaConstraint(a){var b=[],c=_.keys(a.meta);return _.forEach(c,function(c){var d=a.meta[c],e={};if("credential"===c)e.identifier=d;else if(d.is)e[c]=d.is;else throw new Error("Malformed meta constraint \"".concat(c,"\": missing the IS"));b.push(e)}),b}var isMatchCredentialMeta=function(a,b){var c=transformMetaConstraint(b);if(_.isEmpty(c))return!1;var d=function(b){return sift(b)([a])};return c.reduce(function(a,b){return a&&d(b)},!0)};VerifiableCredentialBaseConstructor.CREDENTIAL_META_FIELDS=CREDENTIAL_META_FIELDS,VerifiableCredentialBaseConstructor.getCredentialMeta=getCredentialMeta,VerifiableCredentialBaseConstructor.isMatchCredentialMeta=isMatchCredentialMeta,VerifiableCredentialBaseConstructor.create=function(){var a=(0,_asyncToGenerator2.default)(_regenerator.default.mark(function g(a,b,c,d,e,f){var h,i,j,k,l=arguments;return _regenerator.default.wrap(function(g){for(;;)switch(g.prev=g.next){case 0:return h=6a.maximum&&(b=a.maximum),"undefined"!==a.exclusiveMaximum&&null!==a.exclusiveMaximum&&b>=a.exclusiveMaximum&&(b=a.exclusiveMaximum-.1)),b},generateRandomValueForType=function(a){var b=!!(1=b.t0.statusCode)){b.next=19;break}return b.next=16,services.container.Http.request(d);case 16:e=b.sent,b.next=21;break;case 19:if(!(400>b.t0.statusCode||500<=b.t0.statusCode)){b.next=21;break}throw b.t0;case 21:return b.abrupt("return",e);case 22:case"end":return b.stop();}},b,this,[[5,11]])}));return function(){return a.apply(this,arguments)}}()}]),a}();module.exports=CVCLoader; \ No newline at end of file diff --git a/dist/browser/services/DefaultAnchorServiceImpl.js b/dist/browser/services/DefaultAnchorServiceImpl.js index 267123f7..4d56f947 100644 --- a/dist/browser/services/DefaultAnchorServiceImpl.js +++ b/dist/browser/services/DefaultAnchorServiceImpl.js @@ -1 +1 @@ -'use strict';var _promise=require("babel-runtime/core-js/promise"),_promise2=_interopRequireDefault(_promise),_regenerator=require("babel-runtime/regenerator"),_regenerator2=_interopRequireDefault(_regenerator),_stringify=require("babel-runtime/core-js/json/stringify"),_stringify2=_interopRequireDefault(_stringify),_asyncToGenerator2=require("babel-runtime/helpers/asyncToGenerator"),_asyncToGenerator3=_interopRequireDefault(_asyncToGenerator2);function _interopRequireDefault(a){return a&&a.__esModule?a:{default:a}}var uuid=require("uuid/v4"),_require=require("bitcoinjs-lib"),HDNode=_require.HDNode,ECSignature=_require.ECSignature,sjcl=require("sjcl"),logger=require("../logger");function DummyAnchorServiceImpl(a,b){var c=this;this.config=a,this.http=b;var d=function(){var a=(0,_asyncToGenerator3.default)(_regenerator2.default.mark(function a(b){var e;return _regenerator2.default.wrap(function(a){for(;;)switch(a.prev=a.next){case 0:return a.prev=0,a.next=3,c.http.request({url:b,method:"GET",simple:!0,json:!0});case 3:if(e=a.sent,e&&e.type){a.next=8;break}return a.next=7,d(b);case 7:return a.abrupt("return",a.sent);case 8:if(!(e&&"permanent"!==e.type)){a.next=11;break}return e.statusUrl=b,a.abrupt("return",e);case 11:return a.abrupt("return",e);case 14:throw a.prev=14,a.t0=a["catch"](0),logger.error("Error polling: "+b,(0,_stringify2.default)(a.t0,null,2)),new Error("Error polling: "+b);case 18:case"end":return a.stop();}},a,c,[[0,14]])}));return function(){return a.apply(this,arguments)}}();return this.anchor=(0,_asyncToGenerator3.default)(_regenerator2.default.mark(function a(){var b=0 { if (!_.isEmpty(parameters)) { throw new Error('parameters should be empty'); } + return true; }; + const validateNotEmptyParametersOperators = parameters => { if (!parameters && _.isEmpty(parameters)) { throw new Error('parameters should not be empty'); } + return true; }; + const validatePathParametersOperators = parameters => { if (!_.isString(parameters)) { throw new Error('parameters should be string'); } + return true; }; + const validateNumberParametersOperators = parameters => { if (!_.isNumber(parameters)) { throw new Error('parameters should be number'); } + return true; }; + const validateObjectParametersOperators = parameters => { if (!_.isObject(parameters)) { throw new Error('parameters should be object'); } + return true; }; const sort = (colllection, params) => { const path = _.keys(params)[0]; + const order = params[path]; + const ordered = _.sortBy(colllection, path); + return order === 'ASC' ? ordered : _.reverse(ordered); }; @@ -52,16 +64,19 @@ const AGGREGATION_OPERATORS_MAP = { function aggregate(credentials, stages) { let filtered = [...credentials]; + _.forEach(stages, stage => { const operator = _.keys(stage)[0]; if (!_.includes(_.keys(AGGREGATION_OPERATORS_MAP), operator)) { throw new Error(`Invalid operator: ${operator}`); } + const params = stage[operator]; const operatorImplementation = AGGREGATION_OPERATORS_MAP[operator]; filtered = operatorImplementation(filtered, params); }); + return filtered; } diff --git a/dist/cjs/SecureRandom.js b/dist/cjs/SecureRandom.js index 19756750..151ee708 100644 --- a/dist/cjs/SecureRandom.js +++ b/dist/cjs/SecureRandom.js @@ -1,12 +1,13 @@ -'use strict'; +"use strict"; const sjcl = require('sjcl'); + const logger = require('./logger'); class SecureRandom { constructor(seedHexString) { - logger.debug('Init Secure Random'); - // eslint-disable-next-line new-cap + logger.debug('Init Secure Random'); // eslint-disable-next-line new-cap + this.sjclRandom = new sjcl.prng(10); if (seedHexString) { @@ -17,8 +18,11 @@ class SecureRandom { try { logger.debug('Trying crypto'); /* eslint-disable global-require */ + const hexString = require('crypto').randomBytes(1024).toString('hex'); /* eslint-enable global-require */ + + const seed = sjcl.codec.hex.toBits(hexString); this.sjclRandom.addEntropy(seed, undefined, 'csprng'); this.isSeeded = true; @@ -37,6 +41,7 @@ class SecureRandom { const randomBytes = this.sjclRandom.randomWords(size / 8, 10); return sjcl.codec.hex.fromBits(randomBytes); } + } module.exports = SecureRandom; \ No newline at end of file diff --git a/dist/cjs/claim/Claim.js b/dist/cjs/claim/Claim.js index f6957969..96b29f7d 100644 --- a/dist/cjs/claim/Claim.js +++ b/dist/cjs/claim/Claim.js @@ -1,55 +1,124 @@ -'use strict'; +"use strict"; + +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); }); }; } const _ = require('lodash'); + const sjcl = require('sjcl'); -const { UserCollectableAttribute } = require('@identity.com/uca'); + +const { + UserCollectableAttribute +} = require('../uca/UCA'); + +const { + services +} = require('../services'); + const definitions = require('./definitions'); -const { services } = require('../services'); -const validIdentifiers = _.map(definitions, d => d.identifier); +const { + schemaLoader +} = require('../schemas/jsonSchema'); + +const { + validIdentifiers +} = schemaLoader; + +const findDefinition = (identifier, version) => version ? _.find(definitions, { + identifier, + version +}) : _.find(definitions, { + identifier +}); + +const getDefinition = /*#__PURE__*/function () { + var _ref = _asyncToGenerator(function* (identifier, version) { + yield schemaLoader.loadSchemaFromTitle(identifier); + return findDefinition(identifier, version); + }); -const getDefinition = (identifier, version) => version ? _.find(definitions, { identifier, version }) : _.find(definitions, { identifier }); + return function getDefinition(_x, _x2) { + return _ref.apply(this, arguments); + }; +}(); const isArrayAttestableValue = aValue => aValue.indexOf('[') > -1 && aValue.indexOf(']') > -1; function getBaseIdentifiers(identifier) { - const claimRegex = /claim-cvc:(.*)\.(.*)-v\d*/; + const claimRegex = /^claim-cvc:(.*)\.(.*)-v\d*$/; let isNewIdentifier = true; - let identifierComponents = claimRegex.exec(identifier); + if (identifierComponents === null) { identifierComponents = _.split(identifier, ':'); isNewIdentifier = false; } - return { identifierComponents, isNewIdentifier }; + + return { + identifierComponents, + isNewIdentifier + }; } -function adaptIdentifierIfNeeded(identifier, version) { - const definition = getDefinition(identifier, version); - const resolvedIdentifier = definition && definition.alias ? definition.type : identifier; +function adaptIdentifierIfNeeded(_x3, _x4) { + return _adaptIdentifierIfNeeded.apply(this, arguments); +} - const { isNewIdentifier, identifierComponents } = getBaseIdentifiers(resolvedIdentifier); +function _adaptIdentifierIfNeeded() { + _adaptIdentifierIfNeeded = _asyncToGenerator(function* (identifier, version) { + const definition = yield getDefinition(identifier, version); + const resolvedIdentifier = definition && definition.alias ? definition.type : identifier; + const { + isNewIdentifier, + identifierComponents + } = getBaseIdentifiers(resolvedIdentifier); + const compDefinition = yield getDefinition(resolvedIdentifier, version); + + if (!isNewIdentifier && !compDefinition) { + const newIdentifier = `claim-cvc:${identifierComponents[1]}.${identifierComponents[2]}-v1`; + yield schemaLoader.loadSchemaFromTitle(newIdentifier); + + const foundNewIdentifier = _.find(definitions, { + identifier: newIdentifier + }); + + if (foundNewIdentifier) { + return newIdentifier; + } - if (!isNewIdentifier && !getDefinition(resolvedIdentifier, version)) { - const newIdentifier = `claim-cvc:${identifierComponents[1]}.${identifierComponents[2]}-v1`; - const foundNewIdentifier = _.find(definitions, { identifier: newIdentifier }); - if (foundNewIdentifier) { - return newIdentifier; + throw new Error(`${resolvedIdentifier} is not defined`); } - throw new Error(`${resolvedIdentifier} is not defined`); - } - return identifier; + + return identifier; + }); + return _adaptIdentifierIfNeeded.apply(this, arguments); } class Claim extends UserCollectableAttribute { constructor(identifier, value, version) { - const currentIdentifier = adaptIdentifierIfNeeded(identifier, version); - super(currentIdentifier, value, version, definitions); - this.initialize(currentIdentifier, value, version); + super(identifier, value, version, definitions); + this.initialize(identifier, value, version); + } + + static create(identifier, value, version) { + return _asyncToGenerator(function* () { + const currentIdentifier = yield adaptIdentifierIfNeeded(identifier, version); + + if (!value.attestableValue) { + yield schemaLoader.validateSchema(currentIdentifier, value); + } // Load the schema and it's references from a source to be used for validation and defining the schema definitions + + + yield schemaLoader.loadSchemaFromTitle(currentIdentifier); + return new Claim(currentIdentifier, value, version); + })(); } initialize(identifier, value, version) { super.initialize(identifier, value, version); + if (!this.salt) { const secureRandom = services.container.SecureRandom; this.salt = sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash(secureRandom.wordWith(64))); @@ -57,9 +126,8 @@ class Claim extends UserCollectableAttribute { } initializeValuesWithArrayItems(identifier, values, version) { - const definition = getDefinition(this.identifier, this.version); + const definition = findDefinition(this.identifier, this.version); const ucaArray = []; - if (!_.isArray(values)) throw new Error(`Value for ${identifier}-${version} should be an array`); _.forEach(values, value => { @@ -71,52 +139,64 @@ class Claim extends UserCollectableAttribute { } initializeAttestableValue() { - const { value } = this; - const definition = getDefinition(this.identifier, this.version); - const parsedAttestableValue = Claim.parseAttestableValue(value); + const { + value + } = this; + const definition = findDefinition(this.identifier, this.version); + const parsedAttestableValue = Claim.parseAttestableValue(value); // Trying to construct UCA with a existing attestableValue - // Trying to construct UCA with a existing attestableValue if (parsedAttestableValue.length === 1) { // This is a simple attestableValue this.timestamp = null; this.salt = parsedAttestableValue[0].salt; const ucaValue = parsedAttestableValue[0].value; - this.value = definition.type === 'Array' ? _.map(ucaValue, item => new Claim(definition.items.type, { attestableValue: item })) : this.value = _.includes(['null', 'undefined'], ucaValue) ? null : ucaValue; + this.value = definition.type === 'Array' ? _.map(ucaValue, item => new Claim(definition.items.type, { + attestableValue: item + })) : this.value = _.includes(['null', 'undefined'], ucaValue) ? null : ucaValue; } else { const ucaValue = {}; + for (let i = 0; i < parsedAttestableValue.length; i += 1) { - const { propertyName } = parsedAttestableValue[i]; - // we have stored only the property name on the urn, so we have to find the UCA definition + const { + propertyName + } = parsedAttestableValue[i]; // we have stored only the property name on the urn, so we have to find the UCA definition let filteredIdentifier; let ucaPropertyName; const ucaType = UserCollectableAttribute.resolveType(definition, definitions); const ucaDef = ucaType.properties.find(prop => prop.name === propertyName); + if (ucaDef) { filteredIdentifier = ucaDef.type; ucaPropertyName = propertyName; } else { - const splitPropertyName = propertyName.split('.'); - // this property is used to check if the recursion tree has more than an depth + const splitPropertyName = propertyName.split('.'); // this property is used to check if the recursion tree has more than an depth + const ucaNamespace = splitPropertyName[splitPropertyName.length - 2]; const ucaNamespacePascal = ucaNamespace.substring(0, 1).toUpperCase() + ucaNamespace.substring(1); ucaPropertyName = splitPropertyName[splitPropertyName.length - 1]; filteredIdentifier = `cvc:${ucaNamespacePascal}:${ucaPropertyName}`; - } + } // test if definition exists + - // test if definition exists const filteredDefinition = definitions.find(def => def.identifier === filteredIdentifier); + if (!filteredDefinition) { // this must have an claim path with no recursive definition filteredIdentifier = this.findDefinitionByAttestableValue(ucaPropertyName, definition); } - ucaValue[propertyName] = new Claim(filteredIdentifier, { attestableValue: parsedAttestableValue[i].stringValue }); + + ucaValue[propertyName] = new Claim(filteredIdentifier, { + attestableValue: parsedAttestableValue[i].stringValue + }); } + this.value = ucaValue; } } - /* eslint-disable class-methods-use-this */ + + getValidIdentifiers() { return validIdentifiers; } @@ -127,13 +207,18 @@ class Claim extends UserCollectableAttribute { static parseAttestableArrayValue(value) { const splitDots = value.attestableValue.split(':'); - const propertyName = splitDots[1]; const salt = splitDots[2]; const attestableValueItems = value.attestableValue.substring(value.attestableValue.indexOf('[') + 1, value.attestableValue.indexOf(']') - 1).split(','); - const parsedArrayItems = _.map(attestableValueItems, item => Claim.parseAttestableValue({ attestableValue: item })); + + const parsedArrayItems = _.map(attestableValueItems, item => Claim.parseAttestableValue({ + attestableValue: item + })); + return { - propertyName, salt, value: parsedArrayItems + propertyName, + salt, + value: parsedArrayItems }; } @@ -148,13 +233,16 @@ class Claim extends UserCollectableAttribute { if (isArrayAttestableValue(value.attestableValue)) { const arrayValues = Claim.parseAttestableArrayValue(value); return [arrayValues]; - } + } // If is not an ArrayValue we parse it now + - // If is not an ArrayValue we parse it now const splitPipes = _.split(value.attestableValue, '|'); + const attestableValueRegex = /^urn:(\w+(?:\.\w+)*):(\w+):(.+)/; + _.each(splitPipes, stringValue => { const match = attestableValueRegex.exec(stringValue); + if (match && match.length === 4) { const v = { propertyName: match[1], @@ -165,31 +253,42 @@ class Claim extends UserCollectableAttribute { values.push(v); } }); + if (splitPipes.length !== values.length && splitPipes.length !== values.length + 1) { throw new Error('Invalid attestableValue'); } + return values; } findDefinitionByAttestableValue(attestableValuePropertyName, rootDefinition) { const ucaType = UserCollectableAttribute.resolveType(rootDefinition, definitions); + for (const property of ucaType.properties) { // eslint-disable-line no-restricted-syntax - const resolvedDefinition = _.find(definitions, { identifier: property.type }); + const resolvedDefinition = _.find(definitions, { + identifier: property.type + }); + resolvedDefinition.type = UserCollectableAttribute.resolveType(resolvedDefinition, definitions); + if (!resolvedDefinition.type.properties && property.name === attestableValuePropertyName) { return property.type; } + if (resolvedDefinition.type.properties) { return this.findDefinitionByAttestableValue(attestableValuePropertyName, resolvedDefinition); } } + return null; } getAttestableValue(path, isArrayItem = false) { // all UCA properties they have the form of :propertyName or :something.propertyName - const { identifierComponents } = getBaseIdentifiers(this.identifier); + const { + identifierComponents + } = getBaseIdentifiers(this.identifier); let propertyName = identifierComponents[2]; if (isArrayItem) { @@ -199,32 +298,41 @@ class Claim extends UserCollectableAttribute { if (path) { propertyName = `${path}.${propertyName}`; - } + } // it was defined that the attestable value would be on the URN type https://tools.ietf.org/html/rfc8141 + - // it was defined that the attestable value would be on the URN type https://tools.ietf.org/html/rfc8141 if (['String', 'Number', 'Boolean'].indexOf(this.type) >= 0) { return `urn:${propertyName}:${this.salt}:${this.value}|`; - }if (this.type === 'Array') { + } + + if (this.type === 'Array') { const itemsValues = _.reduce(this.value, (result, item) => `${result}${item.getAttestableValue(null, true)},`, ''); + return `urn:${propertyName}:${this.salt}:[${itemsValues}]`; } + return _.reduce(_.sortBy(_.keys(this.value)), (s, k) => `${s}${this.value[k].getAttestableValue(propertyName)}`, ''); } - /** - * Returns the global CredentialItem of the Credential - */ + * Returns the global CredentialItem of the Credential + */ + + getGlobalIdentifier() { return `claim-${this.identifier}-${this.version}`; } getClaimRootPropertyName() { - const { identifierComponents } = getBaseIdentifiers(this.identifier); + const { + identifierComponents + } = getBaseIdentifiers(this.identifier); return identifierComponents[1].toLowerCase() === 'type' ? '' : _.camelCase(identifierComponents[1]); } getClaimPropertyName() { - const { identifierComponents } = getBaseIdentifiers(this.identifier); + const { + identifierComponents + } = getBaseIdentifiers(this.identifier); return identifierComponents[2]; } @@ -233,8 +341,12 @@ class Claim extends UserCollectableAttribute { } static getPath(identifier) { - const { identifierComponents } = getBaseIdentifiers(identifier); + const { + identifierComponents + } = getBaseIdentifiers(identifier); + const baseName = _.camelCase(identifierComponents[1]); + return baseName !== 'type' ? `${baseName}.${identifierComponents[2]}` : identifierComponents[2]; } @@ -243,15 +355,27 @@ class Claim extends UserCollectableAttribute { const headComponents = head ? _.split(head, '.') : []; let tailComponents = tail ? _.split(tail, '.') : []; tailComponents = _.last(headComponents) === _.first(tailComponents) ? tailComponents.splice(1) : tailComponents; + const newPath = _.join([...headComponents, ...tailComponents], '.'); + return newPath; }; let values = []; - const def = _.find(definitions, { identifier: this.identifier, version: this.version }); + + const def = _.find(definitions, { + identifier: this.identifier, + version: this.version + }); + if (def.credentialItem || def.attestable) { const claimPath = joinPaths(path, !isItemArray ? this.getClaimPath() : null); - values.push({ identifier: this.identifier, value: this.getAttestableValue(null, isItemArray), claimPath }); + values.push({ + identifier: this.identifier, + value: this.getAttestableValue(null, isItemArray), + claimPath + }); + if (this.type === 'Object') { _.forEach(_.keys(this.value), k => { const innerValues = this.value[k].getAttestableValues(claimPath); @@ -263,85 +387,131 @@ class Claim extends UserCollectableAttribute { }); } } + return values; } - /** - * extract the expected Type name for the value when constructing an UCA - * @param {*} definition - */ + * extract the expected Type name for the value when constructing an UCA + * @param {*} definition + */ + + static getTypeName(definition) { if (_.isString(definition.type)) { if (_.includes(validIdentifiers, definition.type)) { - const innerDefinition = _.find(definitions, { identifier: definition.type }); + const innerDefinition = _.find(definitions, { + identifier: definition.type + }); + return this.getTypeName(innerDefinition); } return definition.type; } + return 'Object'; } static getAllProperties(identifier, pathName) { - const definition = _.find(definitions, { identifier }); - const properties = []; - const type = UserCollectableAttribute.resolveType(definition, definitions); - const typeDefinition = _.isString(type) ? _.find(definitions, { identifier: type }) : definition; - - if (typeDefinition && this.getTypeName(typeDefinition) === 'Object') { - let typeDefProps; - if (typeDefinition.type.properties) { - typeDefProps = typeDefinition.type.properties; - } else { - const typeDefDefinition = _.find(definitions, { identifier: typeDefinition.type }); - typeDefProps = UserCollectableAttribute.resolveType(typeDefDefinition, definitions).properties; - } + var _this = this; + + return _asyncToGenerator(function* () { + yield schemaLoader.loadSchemaFromTitle(identifier); - let basePropName; - const { identifierComponents: baseIdentifierComponents } = getBaseIdentifiers(identifier); - if (pathName) { - if (_.includes(pathName, _.lowerCase(baseIdentifierComponents[1]))) { - basePropName = `${pathName}`; + const definition = _.find(definitions, { + identifier + }); + + const properties = []; + const type = UserCollectableAttribute.resolveType(definition, definitions); + const typeDefinition = _.isString(type) ? _.find(definitions, { + identifier: type + }) : definition; + + if (typeDefinition && _this.getTypeName(typeDefinition) === 'Object') { + let typeDefProps; + + if (typeDefinition.type.properties) { + typeDefProps = typeDefinition.type.properties; } else { - basePropName = `${pathName}.${_.lowerCase(baseIdentifierComponents[1])}.${baseIdentifierComponents[2]}`; + const typeDefDefinition = _.find(definitions, { + identifier: typeDefinition.type + }); + + typeDefProps = UserCollectableAttribute.resolveType(typeDefDefinition, definitions).properties; } - } else { - basePropName = `${_.lowerCase(baseIdentifierComponents[1])}.${baseIdentifierComponents[2]}`; - } - if (_.includes(['String', 'Number', 'Boolean'], `${typeDefProps.type}`)) { - // Properties is not an object - properties.push(`${basePropName}.${typeDefProps.name}`); - } else { - _.forEach(typeDefProps, prop => { - const { isNewIdentifier } = getBaseIdentifiers(prop.type); - const newBasePropName = !isNewIdentifier ? basePropName : `${basePropName}.${prop.name}`; - const proProperties = this.getAllProperties(prop.type, newBasePropName); - _.forEach(proProperties, p => properties.push(p)); - }); - } - } else if (pathName) { - const { identifierComponents } = getBaseIdentifiers(definition.identifier); - let propertiesName; - if (pathName.indexOf(identifierComponents[2]) >= 0) { - propertiesName = `${pathName}`; + let basePropName; + const { + identifierComponents: baseIdentifierComponents + } = getBaseIdentifiers(identifier); + + if (pathName) { + if (_.includes(pathName, _.lowerCase(baseIdentifierComponents[1]))) { + basePropName = `${pathName}`; + } else { + basePropName = `${pathName}.${_.lowerCase(baseIdentifierComponents[1])}.${baseIdentifierComponents[2]}`; + } + } else { + basePropName = `${_.lowerCase(baseIdentifierComponents[1])}.${baseIdentifierComponents[2]}`; + } + + if (_.includes(['String', 'Number', 'Boolean'], `${typeDefProps.type}`)) { + // Properties is not an object + properties.push(`${basePropName}.${typeDefProps.name}`); + } else { + const proProperties = yield typeDefProps.reduce( /*#__PURE__*/function () { + var _ref2 = _asyncToGenerator(function* (prev, prop) { + const prevProps = yield prev; + const { + isNewIdentifier + } = getBaseIdentifiers(prop.type); + const newBasePropName = !isNewIdentifier ? basePropName : `${basePropName}.${prop.name}`; + const props = yield _this.getAllProperties(prop.type, newBasePropName); + return [...prevProps, ...props]; + }); + + return function (_x5, _x6) { + return _ref2.apply(this, arguments); + }; + }(), Promise.resolve([])); + properties.push(...proProperties); + } + } else if (pathName) { + const { + identifierComponents + } = getBaseIdentifiers(definition.identifier); + let propertiesName; + + if (pathName.indexOf(identifierComponents[2]) >= 0) { + propertiesName = `${pathName}`; + } else { + propertiesName = `${pathName}.${identifierComponents[2]}`; + } + + properties.push(propertiesName); } else { - propertiesName = `${pathName}.${identifierComponents[2]}`; + const { + identifierComponents + } = getBaseIdentifiers(identifier); + const propertiesName = `${_.lowerCase(identifierComponents[1])}.${identifierComponents[2]}`; + properties.push(propertiesName); } - properties.push(propertiesName); - } else { - const { identifierComponents } = getBaseIdentifiers(identifier); - const propertiesName = `${_.lowerCase(identifierComponents[1])}.${identifierComponents[2]}`; - properties.push(propertiesName); - } - return properties; + + return properties; + })(); } + } function convertIdentifierToClassName(identifier) { - const { identifierComponents } = getBaseIdentifiers(identifier); + const { + identifierComponents + } = getBaseIdentifiers(identifier); const baseName = identifierComponents[1]; + const detailName = _.upperFirst(_.camelCase(identifierComponents[2])); + return `${baseName}${detailName}`; } @@ -350,16 +520,24 @@ function mixinIdentifiers(UCA) { _.forEach(_.filter(definitions, d => d.credentialItem), def => { const name = convertIdentifierToClassName(def.identifier); const source = {}; - const { identifier } = def; + const { + identifier + } = def; function UCAConstructor(value, version) { return new Claim(identifier, value, version); } source[name] = UCAConstructor; + _.mixin(Claim, source); }); + return UCA; } -module.exports = { Claim: mixinIdentifiers(Claim), definitions, getBaseIdentifiers }; \ No newline at end of file +module.exports = { + Claim: mixinIdentifiers(Claim), + definitions, + getBaseIdentifiers +}; \ No newline at end of file diff --git a/dist/cjs/claim/__mocks__/definitions.js b/dist/cjs/claim/__mocks__/definitions.js index 14ead95d..6d376bea 100644 --- a/dist/cjs/claim/__mocks__/definitions.js +++ b/dist/cjs/claim/__mocks__/definitions.js @@ -1,4 +1,4 @@ -'use strict'; +"use strict"; const definitions = [{ identifier: 'civ:Mock:booleans', @@ -29,5 +29,4 @@ const definitions = [{ exclusiveMinimum: false, minimum: 5 }]; - module.exports = definitions; \ No newline at end of file diff --git a/dist/cjs/claim/definitions.js b/dist/cjs/claim/definitions.js index c85b5856..2ceb5fff 100644 --- a/dist/cjs/claim/definitions.js +++ b/dist/cjs/claim/definitions.js @@ -1,618 +1,4 @@ -'use strict'; +"use strict"; -const { definitions: ucaDefinitions } = require('@identity.com/uca'); - -// ######################################### DEFINITIONS ########################################### -const definitions = [{ - identifier: 'claim-cvc:Email.domain-v1', - version: '1', - type: 'cvc:Type:domain', - credentialItem: true -}, { - identifier: 'claim-cvc:Contact.email-v1', - version: '1', - type: 'claim-cvc:Type.email-v1', - credentialItem: true -}, { - identifier: 'claim-cvc:User.id-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:User.realm-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Phone.countryCode-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Phone.number-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Phone.extension-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Phone.lineType-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:PhoneNumber.countryCode-v1', - type: 'claim-cvc:Phone.countryCode-v1', - version: '1', - credentialItem: true -}, { - identifier: 'claim-cvc:PhoneNumber.number-v1', - type: 'claim-cvc:Phone.number-v1', - version: '1', - credentialItem: true -}, { - identifier: 'claim-cvc:PhoneNumber.extension-v1', - type: 'claim-cvc:Phone.extension-v1', - version: '1', - credentialItem: true -}, { - identifier: 'claim-cvc:PhoneNumber.lineType-v1', - type: 'claim-cvc:Phone.lineType-v1', - version: '1', - credentialItem: true -}, { - identifier: 'claim-cvc:Contact.phoneNumber-v1', - version: '1', - type: 'claim-cvc:Type.phoneNumber-v1', - credentialItem: true -}, { - identifier: 'claim-cvc:Name.givenNames-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Name.familyNames-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Name.otherNames-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Type.Name-v1', - version: '1', - type: { - properties: [{ - name: 'givenNames', - type: 'claim-cvc:Name.givenNames-v1' - }, { - name: 'familyNames', - type: 'claim-cvc:Name.familyNames-v1' - }, { - name: 'otherNames', - type: 'claim-cvc:Name.otherNames-v1' - }], - required: ['givenNames'] - }, - credentialItem: true -}, { - identifier: 'claim-cvc:Document.name-v1', - version: '1', - type: 'claim-cvc:Type.Name-v1', - credentialItem: true -}, { - identifier: 'claim-cvc:Identity.name-v1', - version: '1', - type: 'claim-cvc:Type.Name-v1', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.nationality-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.number-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Identity.dateOfBirth-v1', - version: '1', - type: 'cvc:Type:date', - credentialItem: true -}, { - identifier: 'claim-cvc:Address.city-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Address.postalCode-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Address.state-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Address.county-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Address.country-v1', - version: '1', - type: 'cvc:Type:country', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.address-v1', - version: '1', - type: 'claim-cvc:Type.address-v1', - credentialItem: true -}, { - identifier: 'claim-cvc:Identity.address-v1', - version: '1', - type: 'claim-cvc:Type.address-v1', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.dateOfIssue-v1', - version: '1', - type: 'cvc:Type:date', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.dateOfExpiry-v1', - version: '1', - type: 'cvc:Type:date', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.dateOfBirth-v1', - version: '1', - type: 'cvc:Type:date', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.enum-v1', - version: '1', - type: 'String' -}, { - identifier: 'claim-cvc:Document.type-v1', - version: '1', - type: 'cvc:Type:documentType', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.gender-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.issueLocation-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.issueAuthority-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.issueCountry-v1', - version: '1', - type: 'cvc:Type:country', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.placeOfBirth-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Type.email-v1', - version: '1', - type: { - properties: [{ - name: 'username', - type: 'cvc:Email:username' - }, { - name: 'domain', - type: 'claim-cvc:Email.domain-v1' - }] - }, - credentialItem: true -}, { - identifier: 'claim-cvc:Type.phoneNumber-v1', - version: '1', - type: { - properties: [{ - name: 'country', - type: 'claim-cvc:PhoneNumber.country-v1' - }, { - name: 'countryCode', - type: 'claim-cvc:PhoneNumber.countryCode-v1' - }, { - name: 'number', - type: 'claim-cvc:PhoneNumber.number-v1' - }, { - name: 'extension', - type: 'claim-cvc:PhoneNumber.extension-v1' - }, { - name: 'lineType', - type: 'claim-cvc:PhoneNumber.lineType-v1' - }], - required: ['country', 'countryCode', 'number', 'lineType'] - }, - credentialItem: true -}, { - identifier: 'claim-cvc:PhoneNumber.country-v1', - type: 'cvc:Type:country', - version: '1', - credentialItem: true -}, { - identifier: 'claim-cvc:Type.Name-v1', - version: '1', - type: { - properties: [{ - name: 'givenNames', - type: 'claim-cvc:Name.givenNames-v1' - }, { - name: 'familyNames', - type: 'claim-cvc:Name.familyNames-v1' - }, { - name: 'otherNames', - type: 'claim-cvc:Name.otherNames-v1' - }], - required: ['givenNames'] - }, - credentialItem: true -}, { - identifier: 'claim-cvc:Type.address-v1', - version: '1', - type: { - properties: [{ - name: 'country', - type: 'claim-cvc:Address.country-v1' - }, { - name: 'county', - type: 'claim-cvc:Address.county-v1' - }, { - name: 'state', - type: 'claim-cvc:Address.state-v1' - }, { - name: 'street', - type: 'claim-cvc:Address.street-v1' - }, { - name: 'unit', - type: 'claim-cvc:Address.unit-v1' - }, { - name: 'city', - type: 'claim-cvc:Address.city-v1' - }, { - name: 'postalCode', - type: 'claim-cvc:Address.postalCode-v1' - }], - required: ['street', 'city', 'state', 'country'] - }, - credentialItem: true -}, { - identifier: 'claim-cvc:Address.street-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Address.unit-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.properties-v1', - version: '1', - attestable: true, - type: { - properties: [{ - name: 'dateOfIssue', - type: 'claim-cvc:Document.dateOfIssue-v1' - }, { - name: 'dateOfExpiry', - type: 'claim-cvc:Document.dateOfExpiry-v1' - }], - required: ['dateOfIssue'] - }, - credentialItem: true -}, { - identifier: 'claim-cvc:SocialSecurity.number-v1', - version: '1', - type: 'cvc:Type:socialSecurityNumber', - credentialItem: true -}, { - identifier: 'claim-cvc:Validation:evidences.idDocumentFront-v1', - version: '1', - type: 'cvc:Evidences:idDocumentFront', - credentialItem: true -}, { - identifier: 'claim-cvc:Validation:evidences.idDocumentBack-v1', - version: '1', - type: 'cvc:Evidences:idDocumentBack', - credentialItem: true -}, { - identifier: 'claim-cvc:Validation:evidences.selfie-v1', - version: '1', - type: 'cvc:Evidences:selfie', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.evidences-v1', - version: '1', - attestable: true, - type: { - properties: [{ - name: 'idDocumentFront', - type: 'claim-cvc:Validation:evidences.idDocumentFront-v1' - }, { - name: 'idDocumentBack', - type: 'claim-cvc:Validation:evidences.idDocumentBack-v1' - }, { - name: 'selfie', - type: 'claim-cvc:Validation:evidences.selfie-v1' - }] - }, - credentialItem: true -}, { - identifier: 'claim-cvc:Vaccination.date-v1', - version: '1', - type: 'cvc:Type:timestamp', - credentialItem: false -}, { - identifier: 'claim-cvc:Test.date-v1', - version: '1', - type: 'cvc:Type:timestamp', - credentialItem: false -}, { - identifier: 'claim-cvc:Vaccination.name-v1', - version: '1', - type: 'String', - credentialItem: false -}, { - identifier: 'claim-cvc:Vaccination.recordDetail-v1', - version: '1', - type: { - properties: [{ - name: 'createdAt', - type: 'cvc:Type:date' - }, { - name: 'updatedAt', - type: 'cvc:Type:date' - }] - }, - required: ['createdAt'], - credentialItem: false -}, { - identifier: 'claim-cvc:Type.organizationName-v1', - version: '1', - type: 'String', - credentialItem: false -}, { - identifier: 'claim-cvc:Type.organizationId-v1', - version: '1', - type: 'String', - credentialItem: false -}, { - identifier: 'claim-cvc:Type.organization-v1', - version: '1', - type: { - properties: [{ - name: 'name', - type: 'claim-cvc:Type.organizationName-v1' - }, { - name: 'id', - type: 'claim-cvc:Type.organizationId-v1' - }] - }, - required: ['name'], - credentialItem: false -}, { - identifier: 'claim-cvc:Type.patient-v1', - version: '1', - type: { - properties: [{ - name: 'fullName', - type: 'cvc:Type:fullName' - }, { - name: 'dateOfBirth', - type: 'cvc:Type:date' - }] - }, - required: ['name'], - credentialItem: true -}, { - identifier: 'claim-cvc:Medical.code-v1', - version: '1', - type: { - properties: [{ - name: 'name', - type: 'cvc:Code:name' - }, { - name: 'code', - type: 'cvc:Code:code' - }, { - name: 'codeSystem', - type: 'cvc:Code:codeSystem' - }, { - name: 'codeSystemName', - type: 'cvc:Code:codeSystemName' - }] - }, - required: ['name', 'code'], - credentialItem: false -}, { - identifier: 'claim-cvc:Manufacturer.name-v1', - version: '1', - type: 'String', - credentialItem: false -}, { - identifier: 'claim-cvc:Vaccination.manufacturer-v1', - version: '1', - type: { - properties: [{ - name: 'name', - type: 'claim-cvc:Manufacturer.name-v1' - }, { - name: 'code', - type: 'claim-cvc:Medical.code-v1' - }], - required: ['name'] - }, - credentialItem: false -}, { - identifier: 'claim-cvc:Vaccination.id-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Medical.codes-v1', - version: '1', - type: 'Array', - items: { - type: 'claim-cvc:Medical.code-v1' - }, - credentialItem: false -}, { - identifier: 'claim-cvc:Codes.records-v1', - version: '1', - type: 'Array', - items: { - type: 'claim-cvc:Medical.code-v1' - }, - credentialItem: false -}, { - identifier: 'claim-cvc:Vaccination.record-v1', - version: '1', - type: { - properties: [{ - name: 'vaccinationId', - type: 'claim-cvc:Vaccination.id-v1' - }, { - name: 'dateOfAdministration', - type: 'claim-cvc:Vaccination.date-v1' - }, { - name: 'manufacturer', - type: 'claim-cvc:Vaccination.manufacturer-v1' - }, { - name: 'name', - type: 'claim-cvc:Vaccination.name-v1' - }, { - name: 'detail', - type: 'claim-cvc:Vaccination.recordDetail-v1' - }, { - name: 'organization', - type: 'cvc:Type:organization' - }, { - name: 'codes', - type: 'claim-cvc:Codes.records-v1' - }], - required: ['vaccinationId', 'dateOfAdministration', 'name', 'organization'] - }, - credentialItem: true -}, { - identifier: 'claim-cvc:Vaccination.records-v1', - version: '1', - type: 'Array', - items: { - type: 'claim-cvc:Vaccination.record-v1' - }, - credentialItem: true -}, { - identifier: 'claim-cvc:Test.type-v1', - version: '1', - type: 'String', - credentialItem: false -}, { - identifier: 'claim-cvc:Test.result-v1', - version: '1', - type: 'String', - credentialItem: false -}, { - identifier: 'claim-cvc:Test.id-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Test.record-v1', - version: '1', - type: { - properties: [{ - name: 'testId', - type: 'claim-cvc:Test.id-v1' - }, { - name: 'testDate', - type: 'claim-cvc:Test.date-v1' - }, { - name: 'resultDate', - type: 'claim-cvc:Test.date-v1' - }, { - name: 'type', - type: 'claim-cvc:Test.type-v1' - }, { - name: 'result', - type: 'claim-cvc:Test.result-v1' - }, { - name: 'organization', - type: 'claim-cvc:Type.organization-v1' - }, { - name: 'codes', - type: 'claim-cvc:Codes.records-v1' - }], - required: ['testId', 'testDate', 'type', 'result'] - }, - credentialItem: true -}, { - identifier: 'claim-cvc:Test.records-v1', - version: '1', - type: 'Array', - items: { - type: 'claim-cvc:Test.record-v1' - }, - credentialItem: true -}, { - identifier: 'claim-cvc:Medical.covid19-v1', - version: '1', - type: { - properties: [{ - name: 'vaccinations', - type: 'claim-cvc:Vaccination.records-v1' - }, { - name: 'tests', - type: 'claim-cvc:Test.records-v1' - }, { - name: 'patient', - type: 'claim-cvc:Type.patient-v1' - }] - }, - require: ['patient'], - credentialItem: true -}]; - -function transformUcaIdToClaimId(identifier) { - const identifierComponents = identifier.split(':'); - return `claim-cvc:${identifierComponents[1]}.${identifierComponents[2]}-v1`; -} - -function isDefinitionEqual(definition, ucaDefinition) { - return definition.identifier === transformUcaIdToClaimId(ucaDefinition.identifier) || definition.identifier === ucaDefinition.identifier; -} - -ucaDefinitions.forEach(ucaDefinition => { - let found = false; - definitions.some(definition => { - if (isDefinitionEqual(definition, ucaDefinition)) { - found = true; - } - return found; - }); - if (!found) { - definitions.push(ucaDefinition); - } -}); - -module.exports = definitions; \ No newline at end of file +// This exists for backwards compatibility and should be removed +module.exports = []; \ No newline at end of file diff --git a/dist/cjs/constants/headers.js b/dist/cjs/constants/headers.js index 43e468c4..dbf51d9b 100644 --- a/dist/cjs/constants/headers.js +++ b/dist/cjs/constants/headers.js @@ -1,4 +1,4 @@ -'use strict'; +"use strict"; const HeaderType = { EXTERNAL_ID_HEADER: 'X-External-ID', diff --git a/dist/cjs/constants/index.js b/dist/cjs/constants/index.js index c7876b24..a1c7d214 100644 --- a/dist/cjs/constants/index.js +++ b/dist/cjs/constants/index.js @@ -1,4 +1,4 @@ -'use strict'; +"use strict"; const headers = require('./headers'); diff --git a/dist/cjs/creds/ClaimModel.js b/dist/cjs/creds/ClaimModel.js index 38c92e9c..75b378a4 100644 --- a/dist/cjs/creds/ClaimModel.js +++ b/dist/cjs/creds/ClaimModel.js @@ -1,14 +1,16 @@ -'use strict'; +"use strict"; const _ = require('lodash'); - /** * Transforms a list of UCAs into the claim property of the verifiable cliams */ + + class ClaimModel { constructor(ucas) { _.forEach(ucas, uca => { const rootPropertyName = uca.getClaimRootPropertyName(); + if (!_.isEmpty(rootPropertyName)) { if (!this[rootPropertyName]) { this[rootPropertyName] = {}; @@ -20,6 +22,9 @@ class ClaimModel { } }); } + } -module.exports = { ClaimModel }; \ No newline at end of file +module.exports = { + ClaimModel +}; \ No newline at end of file diff --git a/dist/cjs/creds/CredentialSignerVerifier.js b/dist/cjs/creds/CredentialSignerVerifier.js new file mode 100644 index 00000000..8cbf03da --- /dev/null +++ b/dist/cjs/creds/CredentialSignerVerifier.js @@ -0,0 +1,73 @@ +"use strict"; + +const _ = require('lodash'); + +const { + HDNode, + ECSignature +} = require('bitcoinjs-lib'); + +const SIGNATURE_ALGO = 'ec256k1'; + +class CredentialSignerVerifier { + /** + * Creates a new instance of a CredentialSignerVerifier + * + * @param options.keyPair any instace that implements sign and verify interface + * or + * @param options.prvBase58 bse58 serialized private key + * or for verification only + * @param options.pubBase58 bse58 serialized public key + */ + constructor(options) { + if (_.isEmpty(options.keyPair) && _.isEmpty(options.prvBase58) && _.isEmpty(options.pubBase58)) { + throw new Error('Either a keyPair, prvBase58 or pubBase58(to verify only) is required'); + } + + this.keyPair = options.keyPair || HDNode.fromBase58(options.prvBase58 || options.pubBase58); + } + /** + * Verify is a credential has a valid merkletree signature, using a pinned pubkey + * @param credential + * @returns {*|boolean} + */ + + + isSignatureValid(credential) { + if (_.isEmpty(credential.proof) || _.isEmpty(credential.proof.merkleRoot) || _.isEmpty(credential.proof.merkleRootSignature)) { + throw Error('Invalid Credential Proof Schema'); + } + + try { + const signatureHex = _.get(credential, 'proof.merkleRootSignature.signature'); + + const signature = signatureHex ? ECSignature.fromDER(Buffer.from(signatureHex, 'hex')) : null; + + const merkleRoot = _.get(credential, 'proof.merkleRoot'); + + return signature && merkleRoot ? this.keyPair.verify(Buffer.from(merkleRoot, 'hex'), signature) : false; + } catch (error) { + // verify throws in must cases but we want to return false + return false; + } + } + /** + * Create a merkleRootSignature object by signing with a pinned private key + * @param proof + * @returns {{signature, pubBase58: *, algo: string}} + */ + + + sign(proof) { + const hash = Buffer.from(proof.merkleRoot, 'hex'); + const signature = this.keyPair.sign(hash); + return { + algo: SIGNATURE_ALGO, + pubBase58: this.keyPair.neutered().toBase58(), + signature: signature.toDER().toString('hex') + }; + } + +} + +module.exports = CredentialSignerVerifier; \ No newline at end of file diff --git a/dist/cjs/creds/CvcMerkleProof.js b/dist/cjs/creds/CvcMerkleProof.js index a66b65c7..b870e612 100644 --- a/dist/cjs/creds/CvcMerkleProof.js +++ b/dist/cjs/creds/CvcMerkleProof.js @@ -1,62 +1,89 @@ -'use strict'; +"use strict"; const _ = require('lodash'); + const MerkleTools = require('merkle-tools'); -const { sha256 } = require('../lib/crypto'); -const { Claim } = require('../claim/Claim'); -const { services } = require('../services'); +const { + sha256 +} = require('../lib/crypto'); + +const { + Claim +} = require('../claim/Claim'); +const { + services +} = require('../services'); /** * Transforms a list of UCAs into the signature property of the verifiable claims - */ + + class CvcMerkleProof { static get PADDING_INCREMENTS() { return 16; } - constructor(ucas) { + constructor(ucas, credentialSigner = null) { const withRandomUcas = CvcMerkleProof.padTree(ucas); this.type = 'CvcMerkleProof2018'; this.merkleRoot = null; this.anchor = 'TBD (Civic Blockchain Attestation)'; this.leaves = CvcMerkleProof.getAllAttestableValue(withRandomUcas); - this.buildMerkleTree(); + this.buildMerkleTree(credentialSigner); + this.granted = null; } - buildMerkleTree() { + buildMerkleTree(credentialSigner = null) { const merkleTools = new MerkleTools(); + const hashes = _.map(this.leaves, n => sha256(n.value)); + merkleTools.addLeaves(hashes); merkleTools.makeTree(); + _.forEach(hashes, (hash, idx) => { this.leaves[idx].targetHash = hash; this.leaves[idx].node = merkleTools.getProof(idx); }); + this.leaves = _.filter(this.leaves, el => !(el.identifier === 'cvc:Random:node')); this.merkleRoot = merkleTools.getMerkleRoot().toString('hex'); + + if (credentialSigner) { + this.merkleRootSignature = credentialSigner.sign(this); + } } static padTree(nodes) { const currentLength = nodes.length; const targetLength = currentLength < CvcMerkleProof.PADDING_INCREMENTS ? CvcMerkleProof.PADDING_INCREMENTS : _.ceil(currentLength / CvcMerkleProof.PADDING_INCREMENTS) * CvcMerkleProof.PADDING_INCREMENTS; + const newNodes = _.clone(nodes); + const secureRandom = services.container.SecureRandom; + while (newNodes.length < targetLength) { newNodes.push(new Claim('cvc:Random:node', secureRandom.wordWith(16))); } + return newNodes; } static getAllAttestableValue(ucas) { let values = []; + _.forEach(ucas, uca => { const innerValues = uca.getAttestableValues(); values = _.concat(values, innerValues); }); + return values; } + } -module.exports = { CvcMerkleProof }; \ No newline at end of file +module.exports = { + CvcMerkleProof +}; \ No newline at end of file diff --git a/dist/cjs/creds/VerifiableCredential.js b/dist/cjs/creds/VerifiableCredential.js index 7a79f808..2e88a189 100644 --- a/dist/cjs/creds/VerifiableCredential.js +++ b/dist/cjs/creds/VerifiableCredential.js @@ -1,76 +1,84 @@ -'use strict'; +"use strict"; -/** - * Cryptographically secure verify the Credential. - * Performs a non cryptographically secure verification, attestation check and signature validation. - * @param credential - A credential object with expirationDate, claim and proof - * @param verifyAttestationFunc - Async method to verify a credential attestation - * @param verifySignatureFunc - Async method to verify a credential signature - * @return true if verified, false otherwise. - */ -let cryptographicallySecureVerify = (() => { - var _ref = _asyncToGenerator(function* (credential, verifyAttestationFunc, verifySignatureFunc) { - if (!nonCryptographicallySecureVerify(credential)) { - return false; - } - - if (verifyAttestationFunc) { - const attestationCheck = yield verifyAttestationFunc(credential.proof); - if (!attestationCheck) return false; - } - - if (verifySignatureFunc) { - const signatureCheck = yield verifySignatureFunc(credential.proof); - if (!signatureCheck) return false; - } +function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } - return true; - }); +function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } - return function cryptographicallySecureVerify(_x, _x2, _x3) { - return _ref.apply(this, arguments); - }; -})(); - -/** - * Verify if a credential was granted for a specific requester and requestId. - * @param credential - A credential object with expirationDate, claim and proof - * @return true if verified, false otherwise. - */ +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; } +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 gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } +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); }); }; } const _ = require('lodash'); + const validUrl = require('valid-url'); + const sift = require('sift').default; const timestamp = require('unix-timestamp'); + const flatten = require('flat'); + const uuidv4 = require('uuid/v4'); + const MerkleTools = require('merkle-tools'); -const { sha256 } = require('../lib/crypto'); -const { Claim } = require('../claim/Claim'); +const { + sha256 +} = require('../lib/crypto'); + +const { + Claim +} = require('../claim/Claim'); + +const didUtil = require('../lib/did'); const definitions = require('./definitions'); -const { services } = require('../services'); + +const { + services +} = require('../services'); + const time = require('../timeHelper'); -const { CvcMerkleProof } = require('./CvcMerkleProof'); -const { ClaimModel } = require('./ClaimModel'); -// convert a time delta to a timestamp +const { + CvcMerkleProof +} = require('./CvcMerkleProof'); + +const { + ClaimModel +} = require('./ClaimModel'); + +const { + schemaLoader +} = require('../schemas/jsonSchema'); + +const { + parseIdentifier +} = require('../lib/stringUtils'); + +const signerVerifier = require('../lib/signerVerifier'); // convert a time delta to a timestamp + + const convertDeltaToTimestamp = delta => time.applyDeltaToDate(delta).getTime() / 1000; function validIdentifiers() { const vi = _.map(definitions, d => d.identifier); + return vi; } function getClaimsWithFlatKeys(claims) { - const flattenDepth3 = flatten(claims, { maxDepth: 3 }); - const flattenDepth2 = flatten(claims, { maxDepth: 2 }); + const flattenDepth3 = flatten(claims, { + maxDepth: 3 + }); + const flattenDepth2 = flatten(claims, { + maxDepth: 2 + }); + const flattenClaim = _.merge({}, flattenDepth3, flattenDepth2); + return _(flattenClaim).toPairs().sortBy(0).fromPairs().value(); } @@ -81,33 +89,47 @@ function getLeavesClaimPaths(signLeaves) { function verifyLeave(leave, merkleTools, claims, signature, invalidValues, invalidHashs, invalidProofs) { // 1. verify valid targetHashs // 1.1 "leave.value" should be equal claim values - const ucaValue = new Claim(leave.identifier, { attestableValue: leave.value }); + const ucaValue = new Claim(leave.identifier, { + attestableValue: leave.value + }); + + let providedClaimValue = _.get(claims, leave.claimPath); + + if (!providedClaimValue) providedClaimValue = null; + if (ucaValue.type === 'String' || ucaValue.type === 'Number') { - if (ucaValue.value !== _.get(claims, leave.claimPath)) { + if (ucaValue.value !== providedClaimValue) { invalidValues.push(leave.value); } } else if (ucaValue.type === 'Object') { const ucaValueValue = ucaValue.value; - const innerClaimValue = _.get(claims, leave.claimPath); + const innerClaimValue = providedClaimValue; + const claimPathSufix = _.last(_.split(leave.claimPath, '.')); const claimValue = {}; claimValue[claimPathSufix] = innerClaimValue; + const ucaValueKeys = _.keys(ucaValue.value); + _.each(ucaValueKeys, k => { const expectedClaimValue = _.get(claimValue, k); + if (expectedClaimValue && `${_.get(ucaValueValue[k], 'value')}` !== `${expectedClaimValue}`) { invalidValues.push(claimValue[k]); } }); } else if (ucaValue.type === 'Array') { - const innerClaimValue = _.get(claims, leave.claimPath); + const innerClaimValue = providedClaimValue; _.forEach(ucaValue.value, (arrayItem, idx) => { const itemInnerClaimValue = innerClaimValue[idx]; + const ucaValueKeys = _.keys(arrayItem.value); + _.each(ucaValueKeys, k => { const expectedClaimValue = _.get(itemInnerClaimValue, k); + if (expectedClaimValue && `${_.get(arrayItem.value, [k, 'value'])}` !== `${expectedClaimValue}`) { invalidValues.push(itemInnerClaimValue[k]); } @@ -116,28 +138,30 @@ function verifyLeave(leave, merkleTools, claims, signature, invalidValues, inval } else { // Invalid ucaValue.type invalidValues.push(leave.value); - } + } // 1.2 hash(leave.value) should be equal leave.targetHash + - // 1.2 hash(leave.value) should be equal leave.targetHash const hash = sha256(leave.value); - if (hash !== leave.targetHash) invalidHashs.push(leave.targetHash); + if (hash !== leave.targetHash) invalidHashs.push(leave.targetHash); // 2. Validate targetHashs + proofs with merkleRoot - // 2. Validate targetHashs + proofs with merkleRoot const isValidProof = merkleTools.validateProof(leave.node, leave.targetHash, signature.merkleRoot); if (!isValidProof) invalidProofs.push(leave.targetHash); } function validateEvidence(evidenceItem) { const requiredFields = ['type', 'verifier', 'evidenceDocument', 'subjectPresence', 'documentPresence']; + _.forEach(requiredFields, field => { if (!(field in evidenceItem)) { throw new Error(`Evidence ${field} is required`); } - }); - // id property is optional, but if present, SHOULD contain a URL + }); // id property is optional, but if present, SHOULD contain a URL + + if ('id' in evidenceItem && !validUrl.isWebUri(evidenceItem.id)) { throw new Error('Evidence id is not a valid URL'); } + if (!_.isArray(evidenceItem.type)) { throw new Error('Evidence type is not an Array object'); } @@ -157,11 +181,12 @@ function serializeEvidence(evidence) { }; }); } - /** * Transform DSR constraints to sift constraits * @param {*} constraints */ + + function transformConstraint(constraints) { const resultConstraints = []; @@ -169,6 +194,7 @@ function transformConstraint(constraints) { if (!constraint.path) { throw new Error('Malformed contraint: missing PATTH'); } + if (!constraint.is) { throw new Error('Malformed contraint: missing IS'); } @@ -180,185 +206,255 @@ function transformConstraint(constraints) { return resultConstraints; } - /** * Checks if object is a Date Structure (has day, month, year properties) * * @param obj - Structure to test * @return {boolean} */ + + function isDateStructure(obj) { const objKeys = _.keys(obj); + if (objKeys.length !== 3) { // it has more or less keys the (day, month, year) return false; } + return _.includes(objKeys, 'day') && _.includes(objKeys, 'month') && _.includes(objKeys, 'year'); } - /** * Non cryptographically secure verify the Credential * Performs a proofs verification only. * @param credential - A credential object with expirationDate, claim and proof * @return true if verified, false otherwise. */ -function nonCryptographicallySecureVerify(credential) { - const expiry = _.clone(credential.expirationDate); - const claims = _.clone(credential.claim); - const signature = _.clone(credential.proof); - const signLeaves = _.get(signature, 'leaves'); - let valid = false; - - const merkleTools = new MerkleTools(); - const claimsWithFlatKeys = getClaimsWithFlatKeys(claims); - const leavesClaimPaths = getLeavesClaimPaths(signLeaves); - const invalidClaim = []; - const invalidExpiry = []; - const invalidValues = []; - const invalidHashs = []; - const invalidProofs = []; - _.forEach(_.keys(claimsWithFlatKeys), claimKey => { - // check if `claimKey` has a `claimPath` proof - const leaveIdx = _.indexOf(leavesClaimPaths, claimKey); - // if not found - if (leaveIdx === -1) { - // .. still test if parent key node may have a `claimPath` proof - _.findLastIndex(claimKey, '.'); - const parentClaimKey = claimKey.substring(0, _.lastIndexOf(claimKey, '.')); - if (_.indexOf(leavesClaimPaths, parentClaimKey) > -1) { - // if yes, no problem, go to next loop - return; - } - // if no, include on invalidClaim array - invalidClaim.push(claimKey); - } else { - const leave = signLeaves[leaveIdx]; - verifyLeave(leave, merkleTools, claims, signature, invalidValues, invalidHashs, invalidProofs); - } - }); - // It has to be present Credential expiry even with null value - const expiryIdx = _.indexOf(leavesClaimPaths, 'meta.expirationDate'); - if (expiryIdx >= 0) { - const expiryLeave = signLeaves[expiryIdx]; - const metaClaim = { - meta: { - expirationDate: expiry + +function nonCryptographicallySecureVerify(_x) { + return _nonCryptographicallySecureVerify.apply(this, arguments); +} +/** + * Cryptographically secure verify the Credential. + * Performs a non cryptographically secure verification, attestation check and signature validation. + * @param credential - A credential object with expirationDate, claim and proof + * @param verifyAttestationFunc - Async method to verify a credential attestation + * @param verifySignatureFunc - Async method to verify a credential signature + * @return true if verified, false otherwise. + */ + + +function _nonCryptographicallySecureVerify() { + _nonCryptographicallySecureVerify = _asyncToGenerator(function* (credential) { + yield schemaLoader.loadSchemaFromTitle('cvc:Meta:expirationDate'); + yield schemaLoader.loadSchemaFromTitle(credential.identifier); + + const expiry = _.clone(credential.expirationDate); + + const claims = _.clone(credential.credentialSubject); + + const signature = _.clone(credential.proof); + + const signLeaves = _.get(signature, 'leaves'); + + let valid = false; + const merkleTools = new MerkleTools(); + const claimsWithFlatKeys = getClaimsWithFlatKeys(claims); + const leavesClaimPaths = getLeavesClaimPaths(signLeaves); + const invalidClaim = []; + const invalidExpiry = []; + const invalidValues = []; + const invalidHashs = []; + const invalidProofs = []; + + _.forEach(_.keys(claimsWithFlatKeys).filter(key => key !== 'id'), claimKey => { + // check if `claimKey` has a `claimPath` proof + const leaveIdx = _.indexOf(leavesClaimPaths, claimKey); // if not found + + + if (leaveIdx === -1) { + // .. still test if parent key node may have a `claimPath` proof + _.findLastIndex(claimKey, '.'); + + const parentClaimKey = claimKey.substring(0, _.lastIndexOf(claimKey, '.')); + + if (_.indexOf(leavesClaimPaths, parentClaimKey) > -1) { + // if yes, no problem, go to next loop + return; + } // if no, include on invalidClaim array + + + invalidClaim.push(claimKey); + } else { + const leave = signLeaves[leaveIdx]; + verifyLeave(leave, merkleTools, claims, signature, invalidValues, invalidHashs, invalidProofs); } - }; - const totalLengthBefore = invalidValues.length + invalidHashs.length + invalidProofs.length; - verifyLeave(expiryLeave, merkleTools, metaClaim, signature, invalidValues, invalidHashs, invalidProofs); - const totalLengthAfter = invalidValues.length + invalidHashs.length + invalidProofs.length; - if (totalLengthAfter === totalLengthBefore) { - // expiry has always to be string formatted date or null value - // if it is null it means it's indefinitely - if (expiry !== null) { - const now = new Date(); - const expiryDate = new Date(expiry); - if (now.getTime() > expiryDate.getTime()) { - invalidExpiry.push(expiry); + }); // It has to be present Credential expiry even with null value + + + const expiryIdx = _.indexOf(leavesClaimPaths, 'meta.expirationDate'); + + if (expiryIdx >= 0) { + const expiryLeave = signLeaves[expiryIdx]; + const metaClaim = { + meta: { + expirationDate: expiry + } + }; + const totalLengthBefore = invalidValues.length + invalidHashs.length + invalidProofs.length; + verifyLeave(expiryLeave, merkleTools, metaClaim, signature, invalidValues, invalidHashs, invalidProofs); + const totalLengthAfter = invalidValues.length + invalidHashs.length + invalidProofs.length; + + if (totalLengthAfter === totalLengthBefore) { + // expiry has always to be string formatted date or null value + // if it is null it means it's indefinitely + if (expiry !== null) { + const now = new Date(); + const expiryDate = new Date(expiry); + + if (now.getTime() > expiryDate.getTime()) { + invalidExpiry.push(expiry); + } } } } - } - if (_.isEmpty(invalidClaim) && _.isEmpty(invalidValues) && _.isEmpty(invalidHashs) && _.isEmpty(invalidProofs) && _.isEmpty(invalidExpiry)) { - valid = true; - } - return valid; -}function requesterGrantVerify(credential, requesterId, requestId, keyName) { - const label = _.get(credential.proof, 'anchor.subject.label'); - const anchorPubKey = _.get(credential.proof, 'anchor.subject.pub'); - const anchorData = _.get(credential.proof, 'anchor.subject.data'); - if (_.isEmpty(credential.granted) || _.isEmpty(label) || _.isEmpty(anchorPubKey)) { - return false; - } + if (_.isEmpty(invalidClaim) && _.isEmpty(invalidValues) && _.isEmpty(invalidHashs) && _.isEmpty(invalidProofs) && _.isEmpty(invalidExpiry)) { + valid = true; + } - const stringToHash = `${label}${anchorData}${requesterId}${requestId}`; - const hexHash = sha256(stringToHash); + return valid; + }); + return _nonCryptographicallySecureVerify.apply(this, arguments); +} - const cryptoManager = services.container.CryptoManager; +function cryptographicallySecureVerify(_x2, _x3, _x4) { + return _cryptographicallySecureVerify.apply(this, arguments); +} +/** + * Verify if a credential was granted for a specific requester and requestId. + * @param credential - A credential object with expirationDate, claim and proof + * @return true if verified, false otherwise. + */ - let verifyKey = keyName; - if (_.isEmpty(verifyKey)) { - if (!_.isFunction(cryptoManager.installKey)) { - throw new Error('CryptoManager does not support installKey, please use a `keyName` instead.'); + +function _cryptographicallySecureVerify() { + _cryptographicallySecureVerify = _asyncToGenerator(function* (credential, verifyAttestationFunc, verifySignatureFunc) { + const nonCryptographicallyVerified = yield nonCryptographicallySecureVerify(credential); + + if (!nonCryptographicallyVerified) { + return false; + } + + if (verifyAttestationFunc) { + const attestationCheck = yield verifyAttestationFunc(credential.proof); + if (!attestationCheck) return false; + } + + if (verifySignatureFunc) { + const signatureCheck = yield verifySignatureFunc(credential.proof); + if (!signatureCheck) return false; } - verifyKey = `TEMP_KEY_NAME_${new Date().getTime()}`; - cryptoManager.installKey(verifyKey, anchorPubKey); - } - return cryptoManager.verify(verifyKey, hexHash, credential.granted); + return true; + }); + return _cryptographicallySecureVerify.apply(this, arguments); } +function requesterGrantVerify(_x5, _x6, _x7, _x8) { + return _requesterGrantVerify.apply(this, arguments); +} /** * Trasnform {day, month, year } to Unix Date * * @param obj {day, month, year } * @return {number} an unix-timestamp in seconds */ + + +function _requesterGrantVerify() { + _requesterGrantVerify = _asyncToGenerator(function* (credential, requesterId, requestId, keyName) { + const label = _.get(credential.proof, 'anchor.subject.label'); + + const anchorPubKey = _.get(credential.proof, 'anchor.subject.pub'); + + const anchorData = _.get(credential.proof, 'anchor.subject.data'); + + if (_.isEmpty(credential.proof.granted) || _.isEmpty(label) || _.isEmpty(anchorPubKey)) { + return false; + } + + const stringToHash = `${label}${anchorData}${requesterId}${requestId}`; + const hexHash = sha256(stringToHash); + const cryptoManager = services.container.CryptoManager; + let verifyKey = keyName; + + if (_.isEmpty(verifyKey)) { + if (!_.isFunction(cryptoManager.installKey)) { + throw new Error('CryptoManager does not support installKey, please use a `keyName` instead.'); + } + + verifyKey = `TEMP_KEY_NAME_${new Date().getTime()}`; + cryptoManager.installKey(verifyKey, anchorPubKey); + } + + return cryptoManager.verify(verifyKey, hexHash, credential.proof.granted); + }); + return _requesterGrantVerify.apply(this, arguments); +} + function transformDate(obj) { return new Date(obj.year, obj.month - 1, obj.day).getTime() / 1000; } const VERIFY_LEVELS = { - INVALID: -1, // Verifies if the VC structure and/or signature proofs is not valid, or credential is expired - PROOFS: 0, // Verifies if the VC structure and/or signature proofs are valid, including the expiry - ANCHOR: 1, // Verifies if the VC Attestation Anchor structure is valid - GRANTED: 2, // Verifies if the owner granted the VC usage for a specific request + INVALID: -1, + // Verifies if the VC structure and/or signature proofs is not valid, or credential is expired + PROOFS: 0, + // Verifies if the VC structure and/or signature proofs are valid, including the expiry + ANCHOR: 1, + // Verifies if the VC Attestation Anchor structure is valid + GRANTED: 2, + // Verifies if the owner granted the VC usage for a specific request BLOCKCHAIN: 3 // Verifies if the VC Attestation is valid on the blockchain -}; +}; /** * Throws exception if the definition has missing required claims * @param {*} definition - the credential definition * @param {*} ucas - the list of ucas */ + function verifyRequiredClaims(definition, ucas) { if (!_.isEmpty(definition.required)) { const identifiers = ucas.map(uca => uca.identifier); - const missings = _.difference(definition.required, identifiers); - if (!_.isEmpty(missings)) { - throw new Error(`Missing required claim(s): ${_.join(missings, ', ')}`); - } - } -} - -/** - * Throws exception if the definition has missing required claims - * @param {*} definition - the credential definition - * @param {*} verifiableCredentialJSON - the verifiable credential JSON - */ -function verifyRequiredClaimsFromJSON(definition, verifiableCredentialJSON) { - const leaves = _.get(verifiableCredentialJSON, 'proof.leaves'); - if (!_.isEmpty(definition.required) && leaves) { - const identifiers = leaves.map(leave => leave.identifier); const missings = _.difference(definition.required, identifiers); + if (!_.isEmpty(missings)) { throw new Error(`Missing required claim(s): ${_.join(missings, ', ')}`); } } } - /** * Retrieves the credential definition * @param {string} identifier - credential identifier * @param {*} [version] - definition version */ + + function getCredentialDefinition(identifier, version) { - let definition; - if (version) { - definition = _.find(definitions, { identifier, version: `${version}` }); - } else { - definition = _.find(definitions, { identifier }); - } + const definition = _.find(definitions, { + identifier + }); + if (!definition) { throw new Error(`Credential definition for ${identifier} v${version} not found`); } + return definition; } - /** * Creates a new Verifiable Credential based on an well-known identifier and it's claims dependencies * @param {*} identifier @@ -367,9 +463,13 @@ function getCredentialDefinition(identifier, version) { * @param {*} version * @param {*} [evidence] */ -function VerifiableCredentialBaseConstructor(identifier, issuer, expiryIn, ucas, version, evidence) { + + +function VerifiableCredentialBaseConstructor(identifier, issuer, expiryIn, subject, ucas, evidence, signerOptions) { var _this = this; + const parsedIdentifier = parseIdentifier(identifier); + const version = parsedIdentifier ? parsedIdentifier[4] : '1'; this.id = uuidv4(); this.issuer = issuer; const issuerUCA = new Claim('cvc:Meta:issuer', this.issuer); @@ -378,58 +478,63 @@ function VerifiableCredentialBaseConstructor(identifier, issuer, expiryIn, ucas, this.identifier = identifier; this.expirationDate = expiryIn ? timestamp.toDate(timestamp.now(expiryIn)).toISOString() : null; const expiryUCA = new Claim('cvc:Meta:expirationDate', this.expirationDate ? this.expirationDate : 'null'); - const proofUCAs = expiryUCA ? _.concat(ucas, issuerUCA, issuanceDateUCA, expiryUCA) : _.concat(ucas, issuerUCA, issuanceDateUCA); if (!_.includes(validIdentifiers(), identifier)) { throw new Error(`${identifier} is not defined`); } - const definition = getCredentialDefinition(identifier, version); - this.version = `${version}` || definition.version; - this.type = ['Credential', identifier]; + const definition = getCredentialDefinition(identifier, version); // this.version = `${version}` || definition.version; + + this.type = ['VerifiableCredential', 'IdentityCredential']; this.transient = definition.transient || false; if (evidence) { this.evidence = serializeEvidence(evidence); } - // ucas can be empty here if it is been constructed from JSON + this.credentialSubject = { + id: subject + }; // ucas can be empty here if it is been constructed from JSON + if (!_.isEmpty(ucas)) { verifyRequiredClaims(definition, ucas); - this.claim = new ClaimModel(ucas); - this.proof = new CvcMerkleProof(proofUCAs); + this.credentialSubject = _objectSpread(_objectSpread({}, this.credentialSubject), new ClaimModel(ucas)); + this.proof = new CvcMerkleProof(proofUCAs, signerOptions ? signerOptions.signer : null); + if (!_.isEmpty(definition.excludes)) { const removed = _.remove(this.proof.leaves, el => _.includes(definition.excludes, el.identifier)); + _.forEach(removed, r => { - _.unset(this.claim, r.claimPath); + _.unset(this.credentialSubject, r.claimPath); }); } - // The VC Grantted session (see .grantUsageFor) - this.granted = null; } - /** * Returns the global identifier of the Credential */ - this.getGlobalIdentifier = () => `credential-${this.identifier}-${this.version}`; + + this.getGlobalIdentifier = () => `credential-${this.identifier}-${version}`; /** * Creates a filtered credential exposing only the requested claims * @param {*} requestedClaims */ + + this.filter = requestedClaims => { const filtered = _.cloneDeep(this); + _.remove(filtered.proof.leaves, el => !_.includes(requestedClaims, el.identifier)); - filtered.claim = {}; + filtered.credentialSubject = {}; + _.forEach(filtered.proof.leaves, el => { - _.set(filtered.claim, el.claimPath, _.get(this.claim, el.claimPath)); + _.set(filtered.credentialSubject, el.claimPath, _.get(this.credentialSubject, el.claimPath)); }); return filtered; }; - /** * Request that this credential MerkleRoot is anchored on the Blockchain. * This will return a _temporary_ anchor meaning that the blockchain entry is still not confirmed. @@ -448,8 +553,10 @@ function VerifiableCredentialBaseConstructor(identifier, issuer, expiryIn, ucas, * @returns the json object containing the whole anchor attestation * */ - this.requestAnchor = (() => { - var _ref2 = _asyncToGenerator(function* (options) { + + + this.requestAnchor = /*#__PURE__*/function () { + var _ref = _asyncToGenerator(function* (options) { if (_this.transient) { // If credential is transient no Blockchain attestation is issued _this.proof.anchor = { @@ -463,27 +570,30 @@ function VerifiableCredentialBaseConstructor(identifier, issuer, expiryIn, ucas, } const anchorService = services.container.AnchorService; + const updatedOption = _.merge({}, options, { subject: { label: _this.identifier, data: _this.proof.merkleRoot } }); + const anchor = yield anchorService.anchor(updatedOption); _this.proof.anchor = anchor; return _this; }); - return function (_x4) { - return _ref2.apply(this, arguments); + return function (_x9) { + return _ref.apply(this, arguments); }; - })(); - + }(); /** * Trys to renew the current anchor. replecinf the _temporary_ anchor for a _permanent_ one, * already confirmed on the blockchain. */ - this.updateAnchor = _asyncToGenerator(function* () { + + + this.updateAnchor = /*#__PURE__*/_asyncToGenerator(function* () { // If credential is transient no Blockchain attestation is issued if (_this.transient) { // If credential is transient no Blockchain attestation is issued @@ -496,54 +606,75 @@ function VerifiableCredentialBaseConstructor(identifier, issuer, expiryIn, ucas, }; return _this; } + const anchorService = services.container.AnchorService; const anchor = yield anchorService.update(_this.proof.anchor); _this.proof.anchor = anchor; return _this; }); - /** * Iterate over all leaves and see if their proofs are valid * @returns {boolean} */ - this.verifyProofs = () => nonCryptographicallySecureVerify(this); + this.verifyProofs = () => nonCryptographicallySecureVerify(this); /** * Verify the Credential and return a verification level. * @return Any of VC.VERIFY_LEVELS + * @deprecated */ - this.verify = (higherVerifyLevel, options) => { - const { requestorId, requestId, keyName } = options || {}; - const hVerifyLevel = !_.isNil(higherVerifyLevel) ? higherVerifyLevel : VERIFY_LEVELS.GRANTED; - let verifiedlevel = VERIFY_LEVELS.INVALID; - // Test next level - if (verifiedlevel === VERIFY_LEVELS.INVALID && hVerifyLevel >= VERIFY_LEVELS.PROOFS && this.verifyProofs()) verifiedlevel = VERIFY_LEVELS.PROOFS; - // Test next level - if (verifiedlevel === VERIFY_LEVELS.PROOFS && hVerifyLevel >= VERIFY_LEVELS.ANCHOR && this.verifyAttestation()) verifiedlevel = VERIFY_LEVELS.ANCHOR; + this.verify = /*#__PURE__*/function () { + var _ref3 = _asyncToGenerator(function* (higherVerifyLevel, options) { + const { + requestorId, + requestId, + keyName + } = options || {}; + const hVerifyLevel = !_.isNil(higherVerifyLevel) ? higherVerifyLevel : VERIFY_LEVELS.GRANTED; + let verifiedlevel = VERIFY_LEVELS.INVALID; // Test next level - // Test next level - if (verifiedlevel === VERIFY_LEVELS.ANCHOR && hVerifyLevel >= VERIFY_LEVELS.GRANTED && this.verifyGrant(requestorId, requestId, keyName)) verifiedlevel = VERIFY_LEVELS.GRANTED; + if (verifiedlevel === VERIFY_LEVELS.INVALID && hVerifyLevel >= VERIFY_LEVELS.PROOFS && (yield _this.verifyProofs())) verifiedlevel = VERIFY_LEVELS.PROOFS; // Test next level - return verifiedlevel; - }; + if (verifiedlevel === VERIFY_LEVELS.PROOFS && hVerifyLevel >= VERIFY_LEVELS.ANCHOR && _this.verifyAttestation()) verifiedlevel = VERIFY_LEVELS.ANCHOR; // Test next level + if (verifiedlevel === VERIFY_LEVELS.ANCHOR && hVerifyLevel >= VERIFY_LEVELS.GRANTED && _this.verifyGrant(requestorId, requestId, keyName)) verifiedlevel = VERIFY_LEVELS.GRANTED; + return verifiedlevel; + }); + + return function (_x10, _x11) { + return _ref3.apply(this, arguments); + }; + }(); /** * This method checks if the signature matches for the root of the Merkle Tree * @return true or false for the validation */ - this.verifySignature = pinnedPubKey => { + + + this.verifyAnchorSignature = pinnedPubKey => { if (this.proof.anchor.type === 'transient') { return true; } + return services.container.AnchorService.verifySignature(this.proof, pinnedPubKey); }; + /** + * This methods check the stand alone merkletreeSiganture + * return true or false for the validation + */ + + this.verifyMerkletreeSignature = /*#__PURE__*/_asyncToGenerator(function* () { + const verifier = yield signerVerifier.verifier(_this.issuer, _this.proof.merkleRootSignature.verificationMethod); + return verifier.verify(_this); + }); /** * This method checks that the attestation / anchor exists on the BC */ - this.verifyAttestation = _asyncToGenerator(function* () { + + this.verifyAttestation = /*#__PURE__*/_asyncToGenerator(function* () { // Don't check attestation for credentials that are never attested on blockchain if (_this.proof.anchor.type === 'transient' || _this.proof.anchor.network === 'dummynet') { return true; @@ -551,51 +682,57 @@ function VerifiableCredentialBaseConstructor(identifier, issuer, expiryIn, ucas, return services.container.AnchorService.verifyAttestation(_this.proof); }); - /** * This method will revoke the attestation on the chain * @returns {Promise|void>} */ - this.revokeAttestation = _asyncToGenerator(function* () { + + this.revokeAttestation = /*#__PURE__*/_asyncToGenerator(function* () { if (_this.proof.type === 'transient') { return; - } - // eslint-disable-next-line consistent-return + } // eslint-disable-next-line consistent-return + + return services.container.AnchorService.revokeAttestation(_this.proof); }); - /** * This method will check on the chain the balance of the transaction and if it's still unspent, than it's not revoked * @returns {Promise|void>} */ - this.isRevoked = _asyncToGenerator(function* () { + + this.isRevoked = /*#__PURE__*/_asyncToGenerator(function* () { if (_this.proof.type === 'transient') { return false; } + return services.container.AnchorService.isRevoked(_this.proof); }); const convertTimestampIfString = obj => _.isString(obj) ? convertDeltaToTimestamp(obj) : obj; this.isMatch = constraints => { - const claims = _.cloneDeep(this.claim); + const claims = _.cloneDeep(this.credentialSubject); + const siftCompatibleConstraints = transformConstraint(constraints); const claimsMatchConstraint = constraint => { const path = _.keys(constraint)[0]; + const pathValue = _.get(claims, path); + if (isDateStructure(pathValue)) { - _.set(claims, path, transformDate(pathValue)); - // transforms delta values like "-18y" to a proper timestamp + _.set(claims, path, transformDate(pathValue)); // transforms delta values like "-18y" to a proper timestamp + + _.set(constraint, path, _.mapValues(constraint[path], convertTimestampIfString)); - } - // The Constraints are ANDed here - if one is false, the entire + } // The Constraints are ANDed here - if one is false, the entire + + return sift(constraint)([claims]); }; return siftCompatibleConstraints.reduce((matchesAllConstraints, nextConstraint) => matchesAllConstraints && claimsMatchConstraint(nextConstraint), true); }; - /** * Updates the credential with a "granted" token based on the requestorId and a unique requestId (a nonce) that * can be verified later using .verify() function. @@ -606,70 +743,102 @@ function VerifiableCredentialBaseConstructor(identifier, issuer, expiryIn, ucas, * @param {string} option.keyName - A keyName - if CryptoManager is been used. * @param {string} option.pvtKey - A pvtKey in base58 format (default impl). */ - this.grantUsageFor = (requestorId, requestId, { keyName, pvtKey }) => { + + + this.grantUsageFor = (requestorId, requestId, { + keyName, + pvtKey + }) => { if (_.isEmpty(_.get(this.proof, 'anchor.subject.label')) || _.isEmpty(_.get(this.proof, 'anchor.subject.data'))) { throw new Error('Invalid credential attestation/anchor'); } - if (!this.verifySignature()) { + + if (!this.verifyAnchorSignature()) { throw new Error('Invalid credential attestation/anchor signature'); } + if (!requestorId || !requestId || !(keyName || pvtKey)) { throw new Error('Missing required parameter: requestorId, requestId or key'); - } - // eslint-disable-next-line max-len + } // eslint-disable-next-line max-len + + const stringToHash = `${this.proof.anchor.subject.label}${this.proof.anchor.subject.data}${requestorId}${requestId}`; const hexHash = sha256(stringToHash); - const cryptoManager = services.container.CryptoManager; - let signKey = keyName; + if (pvtKey) { if (!_.isFunction(cryptoManager.installKey)) { throw new Error('You provide a `pvtKey` but the CryptoManager does not support it, use a `keyName` instead.'); } + signKey = `TEMP_KEY_NAME_${new Date().getTime()}`; cryptoManager.installKey(signKey, pvtKey); } const hexSign = cryptoManager.sign(signKey, hexHash); - this.granted = hexSign; + this.proof.granted = hexSign; }; + /** + * Serializes the VerifiableCredential to a JSON string + * @param space The number of spaces to indent the JSON with + */ + + + this.toJSON = () => { + const obj = _.pick(this, ['id', 'identifier', 'issuer', 'issuanceDate', 'expirationDate', 'type', 'credentialSubject', 'proof']); // Remove undefined/null values + // eslint-disable-next-line no-restricted-syntax + + for (const k in obj) { + if (obj[k] === null || obj[k] === undefined) { + delete obj[k]; + } + } + + return _objectSpread({ + '@context': ['https://www.w3.org/2018/credentials/v1', `https://www.identity.com/credentials/v${version}`] + }, obj); + }; /** * @param {} requestorId * @param {} requestId * @param {} [keyName] */ + + this.verifyGrant = (requesterId, requestId, keyName) => requesterGrantVerify(this, requesterId, requestId, keyName); return this; } - /** * CREDENTIAL_META_FIELDS - Array with meta fields of a credential */ -const CREDENTIAL_META_FIELDS = ['id', 'identifier', 'issuer', 'issuanceDate', 'expirationDate', 'version', 'type']; + +const CREDENTIAL_META_FIELDS = ['id', 'identifier', 'issuer', 'issuanceDate', 'expirationDate', 'type']; /** * * @param {*} vc */ -const getCredentialMeta = vc => _.pick(vc, CREDENTIAL_META_FIELDS); +const getCredentialMeta = vc => _.pick(vc, CREDENTIAL_META_FIELDS); /** * Sift constraints to throw errors for constraints missing IS * @param {*} constraintsMeta * @param Array */ + + function transformMetaConstraint(constraintsMeta) { - const resultConstraints = []; + const resultConstraints = []; // handle special field constraints.meta.credential - // handle special field constraints.meta.credential const constraintsMetaKeys = _.keys(constraintsMeta.meta); + _.forEach(constraintsMetaKeys, constraintKey => { const constraint = constraintsMeta.meta[constraintKey]; - const siftConstraint = {}; - // handle special field constraints.meta.credential + const siftConstraint = {}; // handle special field constraints.meta.credential + if (constraintKey === 'credential') { siftConstraint.identifier = constraint; } else if (constraint.is) { @@ -677,11 +846,12 @@ function transformMetaConstraint(constraintsMeta) { } else { throw new Error(`Malformed meta constraint "${constraintKey}": missing the IS`); } + resultConstraints.push(siftConstraint); }); + return resultConstraints; } - /** * isMatchCredentialMeta * @param {*} credentialMeta An Object contains only VC meta fields. Other object keys will be ignored. @@ -695,9 +865,10 @@ function transformMetaConstraint(constraintsMeta) { * // } * @returns boolean */ + + const isMatchCredentialMeta = (credentialMeta, constraintsMeta) => { const siftCompatibleConstraints = transformMetaConstraint(constraintsMeta); - if (_.isEmpty(siftCompatibleConstraints)) return false; const credentialMetaMatchesConstraint = constraint => sift(constraint)([credentialMeta]); @@ -708,52 +879,142 @@ const isMatchCredentialMeta = (credentialMeta, constraintsMeta) => { VerifiableCredentialBaseConstructor.CREDENTIAL_META_FIELDS = CREDENTIAL_META_FIELDS; VerifiableCredentialBaseConstructor.getCredentialMeta = getCredentialMeta; VerifiableCredentialBaseConstructor.isMatchCredentialMeta = isMatchCredentialMeta; +/** + * Creates a Verifiable Credential + * + * @param identifier The identifier for the VC (e.g. credential-cvc:Identity-v1) + * @param issuerDid The issuer DID + * @param expiryIn The credential expiry date (nullable) + * @param subject The subject DID + * @param ucas An array of UCAs + * @param evidence The evidence for the credential + * @param signerOptions Signer options: + * @param signerOptions.verificationMethod The verificationMethod for the signing key + * @param signerOptions.keypair The keypair to sign with + * or + * @param signerOptions.privateKey The private key to sign with + * or + * @param signerOptions.signer An object implementing a `sign(CvcMerkleProof)` method + */ + +VerifiableCredentialBaseConstructor.create = /*#__PURE__*/function () { + var _ref8 = _asyncToGenerator(function* (identifier, issuerDid, expiryIn, subject, ucas, evidence, signerOptions = null, validate = true) { + // Load the schema and it's references from a source to be used for validation and defining the schema definitions + yield schemaLoader.loadSchemaFromTitle(identifier); // Load the meta schema's from a source + + yield schemaLoader.loadSchemaFromTitle('cvc:Meta:issuer'); + yield schemaLoader.loadSchemaFromTitle('cvc:Meta:issuanceDate'); + yield schemaLoader.loadSchemaFromTitle('cvc:Meta:expirationDate'); + yield schemaLoader.loadSchemaFromTitle('cvc:Random:node'); + + if (signerOptions) { + const canSignForIssuer = yield didUtil.canSign(issuerDid, signerOptions.verificationMethod); + + if (!canSignForIssuer) { + throw new Error(`The verificationMethod ${signerOptions.verificationMethod} is not allowed to sign for ${issuerDid}`); + } // eslint-disable-next-line no-param-reassign + + + signerOptions.signer = yield signerVerifier.signer(signerOptions); + } + + const vc = new VerifiableCredentialBaseConstructor(identifier, issuerDid, expiryIn, subject, ucas, evidence, signerOptions); + + if (validate) { + yield schemaLoader.validateSchema(identifier, vc.toJSON()); + } + + return vc; + }); + return function (_x12, _x13, _x14, _x15, _x16, _x17) { + return _ref8.apply(this, arguments); + }; +}(); /** * Factory function that creates a new Verifiable Credential based on a JSON object * @param {*} verifiableCredentialJSON * @returns VerifiableCredentialBaseConstructor */ -VerifiableCredentialBaseConstructor.fromJSON = verifiableCredentialJSON => { - const definition = getCredentialDefinition(verifiableCredentialJSON.identifier, verifiableCredentialJSON.version); - - verifyRequiredClaimsFromJSON(definition, verifiableCredentialJSON); - - const newObj = new VerifiableCredentialBaseConstructor(verifiableCredentialJSON.identifier, verifiableCredentialJSON.issuer); - newObj.id = _.clone(verifiableCredentialJSON.id); - newObj.issuanceDate = _.clone(verifiableCredentialJSON.issuanceDate); - newObj.expirationDate = _.clone(verifiableCredentialJSON.expirationDate); - newObj.identifier = _.clone(verifiableCredentialJSON.identifier); - newObj.version = _.clone(verifiableCredentialJSON.version); - newObj.type = _.cloneDeep(verifiableCredentialJSON.type); - newObj.claim = _.cloneDeep(verifiableCredentialJSON.claim); - newObj.proof = _.cloneDeep(verifiableCredentialJSON.proof); - newObj.granted = _.clone(verifiableCredentialJSON.granted) || null; - return newObj; -}; + +VerifiableCredentialBaseConstructor.fromJSON = /*#__PURE__*/function () { + var _ref9 = _asyncToGenerator(function* (verifiableCredentialJSON, partialPresentation = false) { + yield schemaLoader.loadSchemaFromTitle(verifiableCredentialJSON.identifier); + + if (!partialPresentation) { + yield schemaLoader.validateSchema(verifiableCredentialJSON.identifier, verifiableCredentialJSON); + } + + const newObj = yield VerifiableCredentialBaseConstructor.create(verifiableCredentialJSON.identifier, verifiableCredentialJSON.issuer); + newObj.id = _.clone(verifiableCredentialJSON.id); + newObj.issuanceDate = _.clone(verifiableCredentialJSON.issuanceDate); + newObj.expirationDate = _.clone(verifiableCredentialJSON.expirationDate); + newObj.identifier = _.clone(verifiableCredentialJSON.identifier); + newObj.type = _.cloneDeep(verifiableCredentialJSON.type); + newObj.credentialSubject = _.cloneDeep(verifiableCredentialJSON.credentialSubject); + newObj.proof = _.cloneDeep(verifiableCredentialJSON.proof); + return newObj; + }); + + return function (_x18) { + return _ref9.apply(this, arguments); + }; +}(); /** * List all properties of a Verifiable Credential */ -VerifiableCredentialBaseConstructor.getAllProperties = identifier => { - const vcDefinition = _.find(definitions, { identifier }); - if (vcDefinition) { - const allProperties = []; - _.forEach(vcDefinition.depends, ucaIdentifier => { - allProperties.push(...Claim.getAllProperties(ucaIdentifier)); - }); - const excludesProperties = []; - _.forEach(vcDefinition.excludes, ucaIdentifier => { - excludesProperties.push(...Claim.getAllProperties(ucaIdentifier)); + + +VerifiableCredentialBaseConstructor.getAllProperties = /*#__PURE__*/function () { + var _ref10 = _asyncToGenerator(function* (identifier) { + yield schemaLoader.loadSchemaFromTitle(identifier); + + const vcDefinition = _.find(definitions, { + identifier }); - return _.difference(allProperties, excludesProperties); - } - return null; -}; + + if (vcDefinition) { + const allProperties = yield vcDefinition.depends.reduce( /*#__PURE__*/function () { + var _ref11 = _asyncToGenerator(function* (prev, definition) { + const prevProps = yield prev; + const claimProps = yield Claim.getAllProperties(definition); + return [...prevProps, ...claimProps]; + }); + + return function (_x20, _x21) { + return _ref11.apply(this, arguments); + }; + }(), Promise.resolve([])); + let excludesProperties = []; + + if (vcDefinition.excludes) { + excludesProperties = yield vcDefinition.excludes.reduce( /*#__PURE__*/function () { + var _ref12 = _asyncToGenerator(function* (prev, definition) { + const prevProps = yield prev; + const claimProps = yield Claim.getAllProperties(definition); + return [...prevProps, ...claimProps]; + }); + + return function (_x22, _x23) { + return _ref12.apply(this, arguments); + }; + }(), Promise.resolve([])); + } + + return _.difference(allProperties, excludesProperties); + } + + return null; + }); + + return function (_x19) { + return _ref10.apply(this, arguments); + }; +}(); VerifiableCredentialBaseConstructor.VERIFY_LEVELS = VERIFY_LEVELS; VerifiableCredentialBaseConstructor.nonCryptographicallySecureVerify = nonCryptographicallySecureVerify; VerifiableCredentialBaseConstructor.cryptographicallySecureVerify = cryptographicallySecureVerify; VerifiableCredentialBaseConstructor.requesterGrantVerify = requesterGrantVerify; - module.exports = VerifiableCredentialBaseConstructor; \ No newline at end of file diff --git a/dist/cjs/creds/VerifiableCredentialProxy.js b/dist/cjs/creds/VerifiableCredentialProxy.js new file mode 100644 index 00000000..5f530424 --- /dev/null +++ b/dist/cjs/creds/VerifiableCredentialProxy.js @@ -0,0 +1,228 @@ +"use strict"; + +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); }); }; } + +const _ = require('lodash'); + +const VerifiableCredential = require('./VerifiableCredential'); + +const { + schemaLoader +} = require('../schemas/jsonSchema'); + +const CredentialSignerVerifier = require('./CredentialSignerVerifier'); + +const definitions = schemaLoader.credentialDefinitions; +/** + * Retrieves the credential definition + * @param {string} identifier - credential identifier + * @param {*} [version] - definition version + */ + +function getCredentialDefinition(identifier, version) { + const definition = _.find(definitions, { + identifier + }); + + if (!definition) { + throw new Error(`Credential definition for ${identifier} v${version} not found`); + } + + return definition; +} +/** + * Throws exception if the definition has missing required claims + * @param {*} definition - the credential definition + * @param {*} verifiableCredentialJSON - the verifiable credential JSON + */ + + +function verifyRequiredClaimsFromJSON(definition, verifiableCredentialJSON) { + const leaves = _.get(verifiableCredentialJSON, 'proof.leaves'); + + if (!_.isEmpty(definition.required) && leaves) { + const identifiers = leaves.map(leave => leave.identifier); + + const missings = _.difference(definition.required, identifiers); + + if (!_.isEmpty(missings)) { + throw new Error(`Missing required claim(s): ${_.join(missings, ', ')}`); + } + } +} + +class VerifiableCredentialProxy extends VerifiableCredential { + get claim() { + return this.credentialSubject; + } + + get granted() { + return this.proof && this.proof.granted ? this.proof.granted : null; + } + + set granted(granted) { + this.proof.granted = granted; + } + + constructor(identifier, issuer, expiryIn, ucas, version, evidence, signerVerifier = null) { + super(identifier, issuer, expiryIn, null, ucas, evidence, signerVerifier); + this.version = version; + /** + * Returns the old format VC when converting to JSON + */ + + this.toJSON = () => { + const obj = { + id: _.clone(this.id), + identifier: _.clone(this.identifier), + issuer: _.clone(this.issuer), + issuanceDate: _.clone(this.issuanceDate), + expirationDate: _.clone(this.expirationDate), + version: _.clone(this.version), + type: ['Credential', this.identifier], + claim: _.clone(this.credentialSubject), + proof: _.clone(this.proof) + }; + if (obj.claim) delete obj.claim.id; + return obj; + }; // maintains the old verification process for the older VC format + + + this.verifyMerkletreeSignature = pubBase58 => { + if (_.isEmpty(pubBase58)) return false; + const verifier = new CredentialSignerVerifier({ + pubBase58 + }); + return verifier.isSignatureValid(this); + }; + } + +} + +VerifiableCredentialProxy.create = /*#__PURE__*/function () { + var _ref = _asyncToGenerator(function* (identifier, issuer, expiryIn, ucas, version, evidence, signerVerifier = null) { + // Load the schema and it's references from a source to be used for validation and defining the schema definitions + const schema = yield schemaLoader.loadSchemaFromTitle(identifier); // Wrap the old signer verifier for backwards compatibility + + let signer; + + if (signerVerifier) { + signer = { + signer: signerVerifier + }; + } // If it has a credentialSubject, use the new VC format + + + if (schema && schema.properties.credentialSubject) { + return VerifiableCredential.create(identifier, issuer, expiryIn, '', ucas, evidence, signer); + } // Load the meta schema's from a source + + + yield schemaLoader.loadSchemaFromTitle('cvc:Meta:issuer'); + yield schemaLoader.loadSchemaFromTitle('cvc:Meta:issuanceDate'); + yield schemaLoader.loadSchemaFromTitle('cvc:Meta:expirationDate'); + yield schemaLoader.loadSchemaFromTitle('cvc:Random:node'); + return new VerifiableCredentialProxy(identifier, issuer, expiryIn, ucas, version, evidence, signer); + }); + + return function (_x, _x2, _x3, _x4, _x5, _x6) { + return _ref.apply(this, arguments); + }; +}(); +/** + * Factory function that creates a new Verifiable Credential based on a JSON object. + * + * This proxy function ensures that the VC is converted into the new format. + * @param {*} verifiableCredentialJSON + * @returns VerifiableCredentialBaseConstructor + */ + + +VerifiableCredentialProxy.fromJSON = /*#__PURE__*/function () { + var _ref2 = _asyncToGenerator(function* (verifiableCredentialJSON, partialPresentation = false) { + const schema = yield schemaLoader.loadSchemaFromTitle(verifiableCredentialJSON.identifier); + const properties = yield schemaLoader.flattenCredentialSchemaProperties(schema); + + if (properties.credentialSubject) { + return VerifiableCredential.fromJSON(verifiableCredentialJSON); + } + + const newObj = yield VerifiableCredentialProxy.create(verifiableCredentialJSON.identifier, verifiableCredentialJSON.issuer); + newObj.id = _.clone(verifiableCredentialJSON.id); + newObj.issuanceDate = _.clone(verifiableCredentialJSON.issuanceDate); + newObj.expirationDate = _.clone(verifiableCredentialJSON.expirationDate); + newObj.identifier = _.clone(verifiableCredentialJSON.identifier); + newObj.version = _.clone(verifiableCredentialJSON.version); + newObj.type = ['VerifiableCredential', 'IdentityCredential']; + newObj.credentialSubject = _.cloneDeep(verifiableCredentialJSON.claim); + newObj.proof = _.cloneDeep(verifiableCredentialJSON.proof); + + if (!partialPresentation) { + const definition = getCredentialDefinition(verifiableCredentialJSON.identifier, verifiableCredentialJSON.version); + verifyRequiredClaimsFromJSON(definition, verifiableCredentialJSON); + } + + return newObj; + }); + + return function (_x7) { + return _ref2.apply(this, arguments); + }; +}(); +/** + * Non cryptographically secure verify the Credential + * Performs a proofs verification only. + * + * This proxy function ensures that if the VC is provided as a value object, it is correctly converted + * @param credential - A credential object with expirationDate, claim and proof + * @return true if verified, false otherwise. + */ + + +VerifiableCredentialProxy.nonCryptographicallySecureVerify = /*#__PURE__*/function () { + var _ref3 = _asyncToGenerator(function* (credential) { + const vc = yield VerifiableCredentialProxy.fromJSON(credential); + return VerifiableCredential.nonCryptographicallySecureVerify(vc); + }); + + return function (_x8) { + return _ref3.apply(this, arguments); + }; +}(); +/** + * Cryptographically secure verify the Credential. + * Performs a non cryptographically secure verification, attestation check and signature validation. + * + * This proxy function ensures that if the VC is provided as a value object, it is correctly converted + * @param credential - A credential object with expirationDate, claim and proof + * @param verifyAttestationFunc - Async method to verify a credential attestation + * @param verifySignatureFunc - Async method to verify a credential signature + * @return true if verified, false otherwise. + */ + + +VerifiableCredentialProxy.cryptographicallySecureVerify = /*#__PURE__*/function () { + var _ref4 = _asyncToGenerator(function* (credential, verifyAttestationFunc, verifySignatureFunc) { + const vc = yield VerifiableCredentialProxy.fromJSON(credential); + return VerifiableCredential.cryptographicallySecureVerify(vc, verifyAttestationFunc, verifySignatureFunc); + }); + + return function (_x9, _x10, _x11) { + return _ref4.apply(this, arguments); + }; +}(); + +VerifiableCredentialProxy.requesterGrantVerify = /*#__PURE__*/function () { + var _ref5 = _asyncToGenerator(function* (credential, requesterId, requestId, keyName) { + const vc = yield VerifiableCredentialProxy.fromJSON(credential); + return VerifiableCredential.requesterGrantVerify(vc, requesterId, requestId, keyName); + }); + + return function (_x12, _x13, _x14, _x15) { + return _ref5.apply(this, arguments); + }; +}(); + +module.exports = VerifiableCredentialProxy; \ No newline at end of file diff --git a/dist/cjs/creds/__mocks__/definitions.js b/dist/cjs/creds/__mocks__/definitions.js index 6112924d..a22ae549 100644 --- a/dist/cjs/creds/__mocks__/definitions.js +++ b/dist/cjs/creds/__mocks__/definitions.js @@ -1,4 +1,4 @@ -'use strict'; +"use strict"; const definitions = [{ identifier: 'civ:Credential:SimpleIdentity', @@ -14,5 +14,4 @@ const definitions = [{ depends: ['civ:Identity:name', 'civ:Identity:DateOfBirth'], excludes: ['civ:Identity:name.middle'] }]; - module.exports = definitions; \ No newline at end of file diff --git a/dist/cjs/creds/definitions.js b/dist/cjs/creds/definitions.js index 333a5cf7..2ceb5fff 100644 --- a/dist/cjs/creds/definitions.js +++ b/dist/cjs/creds/definitions.js @@ -1,68 +1,4 @@ -'use strict'; +"use strict"; -/** - * name: 'attgenericId', - * name: 'attBaseIdentity', - * name: 'attAddress', - * @type {*[]} - */ -const definitions = [{ - identifier: 'credential-cvc:Email-v1', - version: '1', - depends: ['claim-cvc:Contact.email-v1'] -}, { - identifier: 'credential-cvc:PhoneNumber-v1', - version: '1', - depends: ['claim-cvc:Contact.phoneNumber-v1'] -}, { - identifier: 'credential-cvc:GenericDocumentId-v1', - version: '1', - depends: ['claim-cvc:Document.type-v1', 'claim-cvc:Document.number-v1', 'claim-cvc:Document.name-v1', 'claim-cvc:Document.gender-v1', 'claim-cvc:Document.issueLocation-v1', 'claim-cvc:Document.issueAuthority-v1', 'claim-cvc:Document.issueCountry-v1', 'claim-cvc:Document.placeOfBirth-v1', 'claim-cvc:Document.dateOfBirth-v1', 'claim-cvc:Document.address-v1', 'claim-cvc:Document.properties-v1', 'cvc:Document:image'] -}, { - identifier: 'credential-cvc:IdDocument-v1', - version: '1', - depends: ['claim-cvc:Document.type-v1', 'claim-cvc:Document.number-v1', 'claim-cvc:Document.name-v1', 'claim-cvc:Document.gender-v1', 'claim-cvc:Document.issueCountry-v1', 'claim-cvc:Document.placeOfBirth-v1', 'claim-cvc:Document.dateOfBirth-v1', 'claim-cvc:Document.dateOfExpiry-v1', 'claim-cvc:Document.nationality-v1', 'claim-cvc:Document.evidences-v1'], - required: ['claim-cvc:Document.type-v1', 'claim-cvc:Document.name-v1', 'claim-cvc:Document.dateOfBirth-v1', 'claim-cvc:Document.issueCountry-v1'] -}, { - identifier: 'credential-cvc:IdDocument-v2', - version: '1', - depends: ['claim-cvc:Document.type-v1', 'claim-cvc:Document.number-v1', 'claim-cvc:Document.name-v1', 'claim-cvc:Document.gender-v1', 'claim-cvc:Document.issueCountry-v1', 'claim-cvc:Document.placeOfBirth-v1', 'claim-cvc:Document.dateOfBirth-v1', 'claim-cvc:Document.dateOfExpiry-v1', 'claim-cvc:Document.nationality-v1', 'claim-cvc:Document.evidences-v1'], - required: ['claim-cvc:Document.type-v1', 'claim-cvc:Document.name-v1', 'claim-cvc:Document.dateOfBirth-v1', 'claim-cvc:Document.issueCountry-v1', 'claim-cvc:Document.evidences-v1'] -}, { - identifier: 'credential-cvc:LiveIdDocument-v1', - version: '1', - depends: ['claim-cvc:Document.type-v1', 'claim-cvc:Document.number-v1', 'claim-cvc:Document.name-v1', 'claim-cvc:Document.gender-v1', 'claim-cvc:Document.issueCountry-v1', 'claim-cvc:Document.placeOfBirth-v1', 'claim-cvc:Document.dateOfBirth-v1', 'claim-cvc:Document.dateOfExpiry-v1', 'claim-cvc:Document.nationality-v1', 'claim-cvc:Document.evidences-v1'], - required: ['claim-cvc:Document.type-v1', 'claim-cvc:Document.name-v1', 'claim-cvc:Document.dateOfBirth-v1', 'claim-cvc:Document.issueCountry-v1', 'claim-cvc:Document.evidences-v1'] -}, { - identifier: 'credential-cvc:Address-v1', - version: '1', - depends: ['claim-cvc:Identity.address-v1'] -}, { - identifier: 'credential-cvc:Identity-v1', - version: '1', - depends: ['claim-cvc:Identity.name-v1', 'claim-cvc:Identity.dateOfBirth-v1'] -}, { - identifier: 'credential-cvc:IDVaaS-v1', - version: '1', - depends: ['claim-cvc:Identity.name-v1', 'claim-cvc:Identity.dateOfBirth-v1', 'claim-cvc:Identity.address-v1', 'claim-cvc:Contact.email-v1', 'claim-cvc:SocialSecurity.number-v1', 'claim-cvc:Contact.phoneNumber-v1'] -}, { - identifier: 'credential-cvc:UnverifiedSsn-v1', - version: '1', - transient: true, - depends: ['claim-cvc:SocialSecurity.number-v1'] -}, { - identifier: 'credential-cvc:UnverifiedAddress-v1', - version: '1', - transient: true, - depends: ['claim-cvc:Identity.address-v1'] -}, { - identifier: 'credential-cvc:Covid19-v1', - version: '1', - depends: ['claim-cvc:Medical.covid19-v1'] -}, { - identifier: 'credential-alt:Identity-v1', - version: '1', - depends: ['claim-cvc:Identity.name-v1', 'claim-cvc:Identity.dateOfBirth-v1', 'claim-cvc:Identity.address-v1'] -}]; - -module.exports = definitions; \ No newline at end of file +// This exists for backwards compatibility and should be removed +module.exports = []; \ No newline at end of file diff --git a/dist/cjs/errors/definitions.js b/dist/cjs/errors/definitions.js index 10674b71..5e2f0d2f 100644 --- a/dist/cjs/errors/definitions.js +++ b/dist/cjs/errors/definitions.js @@ -1,4 +1,4 @@ -'use strict'; +"use strict"; /** * Enum for ErrorCodes @@ -6,9 +6,9 @@ * @enum { string } */ const ErrorCodes = { - // IDV // Manual Review Tool + /** * Reason: Manual Review detected that the provided document is invalid. * Troubleshooting: Make sure the provided document is a valid one. @@ -50,8 +50,8 @@ const ErrorCodes = { * Troubleshooting: Make sure the provided document do fit the requirements. */ ERROR_IDV_MRT_REQUIREMENTS_FAIL: 'error.idv.mrt.requirements.fail', - // Validation + /** * Reason: The IDV detected that the provided document is invalid. * Troubleshooting: Make sure the provided document is a valid one. @@ -182,6 +182,13 @@ const ErrorCodes = { */ ERROR_IDV_CREDENTIAL_INVALID_SIGNATURE: 'error.idv.credential.invalid.signature', + /** + * Reason: Could not anchor the credential, + * possibly caused by errors while connecting to an external provider + * Troubleshooting: Try again later + */ + ERROR_IDV_CREDENTIAL_FAILED_ANCHORING: 'error.idv.credential.failed.anchoring', + /** * Reason: The credential has already been signed. * Troubleshooting: The credential is already signed. You must not sign it again @@ -267,7 +274,6 @@ const ErrorCodes = { * Troubleshooting: Make sure to provide the received token */ ERROR_IDV_TOKEN_MISMATCH: 'error.idv.token.mismatch', - // CW ERRORS /** @@ -419,14 +425,13 @@ const ErrorCodes = { * Troubleshooting: Retry the network call again */ ERROR_RETRYABLE_NETWORK_ERROR: 'error.retryable.network' - }; - /** * Enum for ErrorContextTypes * @readonly * @enum { string } */ + const ErrorContextTypes = { MISSING_PROPERTY: 'missing_property', UCA_STATE: 'uca_state', @@ -439,7 +444,6 @@ const ErrorContextTypes = { CREDENTIAL_ITEM: 'credential_item', UCA_ERROR: 'uca_error' }; - module.exports = { ErrorCodes, ErrorContextTypes diff --git a/dist/cjs/errors/idvErrors.js b/dist/cjs/errors/idvErrors.js index 8269d482..503646dc 100644 --- a/dist/cjs/errors/idvErrors.js +++ b/dist/cjs/errors/idvErrors.js @@ -1,13 +1,16 @@ -'use strict'; +"use strict"; const _ = require('lodash'); -const { ErrorCodes, ErrorContextTypes } = require('./definitions'); -// These codes are passed in the 'name' value of the error object when the IDV-toolkit +const { + ErrorCodes, + ErrorContextTypes +} = require('./definitions'); // These codes are passed in the 'name' value of the error object when the IDV-toolkit // throws an error // @deprecated - left here for retrofit, use ErrorConstants instead for future versions -const IDVErrorCodes = _.pickBy(ErrorCodes, (v, k) => k.startsWith('ERROR_IDV')); + +const IDVErrorCodes = _.pickBy(ErrorCodes, (v, k) => k.startsWith('ERROR_IDV')); /* * IDVError parses a HTTP Error response body from the IDV-toolkit * Usage: the IDVError can be instantiated directly from the HTTPResponse body e.g. @@ -19,12 +22,15 @@ const IDVErrorCodes = _.pickBy(ErrorCodes, (v, k) => k.startsWith('ERROR_IDV')); * [{ name: ErrorContextType.MISSING_PROPERTY, value: missingProperty }] * @returns an instance of IDVError * */ + + class IDVError { constructor(errorObj) { this.message = errorObj.message; this.errorCode = errorObj.name; this.errorContext = errorObj.data; } + } module.exports = { diff --git a/dist/cjs/errors/index.js b/dist/cjs/errors/index.js index c9e155b4..3efef77b 100644 --- a/dist/cjs/errors/index.js +++ b/dist/cjs/errors/index.js @@ -1,10 +1,15 @@ -'use strict'; +"use strict"; const idvErrors = require('./idvErrors'); -const { ErrorCodes, ErrorContextTypes } = require('./definitions'); + +const { + ErrorCodes, + ErrorContextTypes +} = require('./definitions'); module.exports = { ErrorCodes, ErrorContextTypes, idvErrors // For retrofit, will be deprecated on the future + }; \ No newline at end of file diff --git a/dist/cjs/index.js b/dist/cjs/index.js index d68a0c06..ab86077e 100644 --- a/dist/cjs/index.js +++ b/dist/cjs/index.js @@ -1,24 +1,50 @@ -'use strict'; +"use strict"; + +const { + Claim +} = require('./claim/Claim'); + +const { + UserCollectableAttribute +} = require('./uca/UCA'); -const { Claim } = require('./claim/Claim'); const VC = require('./creds/VerifiableCredential'); -const { initServices, services } = require('./services/index'); + +const { + initServices, + services +} = require('./services/index'); + const isValidGlobalIdentifier = require('./isValidGlobalIdentifier'); + const isClaimRelated = require('./isClaimRelated'); + const errors = require('./errors'); + const constants = require('./constants'); + const claimDefinitions = require('./claim/definitions'); + const credentialDefinitions = require('./creds/definitions'); + const aggregate = require('./AggregationHandler'); +const { + schemaLoader +} = require('./schemas/jsonSchema'); + +const CVCSchemaLoader = require('./schemas/jsonSchema/loaders/cvc'); + +const VCCompat = require('./creds/VerifiableCredentialProxy'); /** * Entry Point for Civic Credential Commons * @returns {CredentialCommons} * @constructor */ + + function CredentialCommons() { this.Claim = Claim; - this.VC = VC; this.init = initServices; this.isValidGlobalIdentifier = isValidGlobalIdentifier; this.isClaimRelated = isClaimRelated; @@ -28,8 +54,13 @@ function CredentialCommons() { this.constants = constants; this.claimDefinitions = claimDefinitions; this.credentialDefinitions = credentialDefinitions; + this.schemaLoader = schemaLoader; + this.CVCSchemaLoader = CVCSchemaLoader; + this.UserCollectableAttribute = UserCollectableAttribute; + this.VC = VC; + this.VCCompat = VCCompat; return this; -} +} // to work with entry points in multi module manage the best way + -// to work with entry points in multi module manage the best way module.exports = new CredentialCommons(); \ No newline at end of file diff --git a/dist/cjs/isClaimRelated.js b/dist/cjs/isClaimRelated.js index af2c7d5b..1bc18c08 100644 --- a/dist/cjs/isClaimRelated.js +++ b/dist/cjs/isClaimRelated.js @@ -1,8 +1,21 @@ -'use strict'; +"use strict"; + +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); }); }; } const _ = require('lodash'); -const { definitions, Claim } = require('./claim/Claim'); + +const { + definitions, + Claim +} = require('./claim/Claim'); + const vcDefinitions = require('./creds/definitions'); + +const { + schemaLoader +} = require('./schemas/jsonSchema'); /** * Validate an claim path against it's parent UserCollectableAttribute, and the parent Claim against the * dependencies of an Credential @@ -11,31 +24,47 @@ const vcDefinitions = require('./creds/definitions'); * @param credential the parent identifier, eg: civ:Credential:GenericId * @return true if the dependency exists and false if it doesn't */ -function isClaimRelated(claim, uca, credential) { - // first get the UCA identifier - const ucaIdentifier = uca.substring(uca.indexOf('-') + 1, uca.lastIndexOf('-')); - // check on the credential commons if this identifier exists - const ucaDefinition = definitions.find(definition => definition.identifier === ucaIdentifier); - // does the UCA exist? - if (ucaDefinition) { - const ucaProperties = Claim.getAllProperties(ucaIdentifier); - - // does the claim exists in the Claim? - if (_.includes(ucaProperties, claim)) { - // we now have the composite uca, the uca for the claim property, they both are correct - // we need to check now the UCA is inside the dependencies of the credential refered as parent - const credentialDefinition = vcDefinitions.find(definition => definition.identifier === credential); - if (credentialDefinition) { - return _.includes(credentialDefinition.depends, ucaIdentifier); + + +function isClaimRelated(_x, _x2, _x3) { + return _isClaimRelated.apply(this, arguments); +} + +function _isClaimRelated() { + _isClaimRelated = _asyncToGenerator(function* (claim, uca, credential) { + // Load the schema and it's references from a source to be used for validation and defining the schema definitions + yield schemaLoader.loadSchemaFromTitle(claim); + yield schemaLoader.loadSchemaFromTitle(uca); + yield schemaLoader.loadSchemaFromTitle(credential); // first get the UCA identifier + + const ucaIdentifier = uca.substring(uca.indexOf('-') + 1, uca.lastIndexOf('-')); // Load the schema and it's references from a source to be used for validation and defining the schema definitions + + yield schemaLoader.loadSchemaFromTitle(ucaIdentifier); // check on the credential commons if this identifier exists + + const ucaDefinition = definitions.find(definition => definition.identifier === ucaIdentifier); // does the UCA exist? + + if (ucaDefinition) { + const ucaProperties = yield Claim.getAllProperties(ucaIdentifier); // does the claim exists in the Claim? + + if (_.includes(ucaProperties, claim)) { + // we now have the composite uca, the uca for the claim property, they both are correct + // we need to check now the UCA is inside the dependencies of the credential refered as parent + const credentialDefinition = vcDefinitions.find(definition => definition.identifier === credential); + + if (credentialDefinition) { + return _.includes(credentialDefinition.depends, ucaIdentifier); + } + + throw new Error('Credential identifier does not exist'); + } else { + throw new Error('Claim property path does not exist on UCA definitions'); } - throw new Error('Credential identifier does not exist'); } else { - throw new Error('Claim property path does not exist on UCA definitions'); + // return error about wrong uca identifier + throw new Error('UCA identifier does not exist'); } - } else { - // return error about wrong uca identifier - throw new Error('UCA identifier does not exist'); - } + }); + return _isClaimRelated.apply(this, arguments); } module.exports = isClaimRelated; \ No newline at end of file diff --git a/dist/cjs/isValidGlobalIdentifier.js b/dist/cjs/isValidGlobalIdentifier.js index 1140dbc6..272c7f3e 100644 --- a/dist/cjs/isValidGlobalIdentifier.js +++ b/dist/cjs/isValidGlobalIdentifier.js @@ -1,40 +1,59 @@ -'use strict'; +"use strict"; + +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); }); }; } const _ = require('lodash'); -const { definitions } = require('@identity.com/uca'); -const vcDefinitions = require('./creds/definitions'); -const claimDefinitions = require('./claim/definitions'); -const validUCAIdentifiers = _.map(definitions, d => d.identifier); -const validClaimIdentifiers = _.map(claimDefinitions, d => d.identifier); -const validVCIdentifiers = _.map(vcDefinitions, d => d.identifier); +const { + schemaLoader +} = require('./schemas/jsonSchema'); + +const validUCAIdentifiers = schemaLoader.validIdentifiers; +const validClaimIdentifiers = schemaLoader.validIdentifiers; +const validVCIdentifiers = schemaLoader.validCredentialIdentifiers; const validPrefixes = ['claim', 'credential']; -function isValidGlobalIdentifier(identifier) { - const splited = _.split(identifier, '-'); - - if (splited.length !== 3) { - throw new Error('Malformed Global Identifier'); - } - - if (!_.includes(validPrefixes, splited[0])) { - throw new Error('Invalid Global Identifier Prefix'); - } - - switch (splited[0]) { - case 'claim': - if (!_.includes(validUCAIdentifiers, splited[1]) && !_.includes(validClaimIdentifiers, identifier)) { - throw new Error(`${identifier} is not valid`); - } - return true; - case 'credential': - if (!_.includes(validVCIdentifiers, splited[1]) && !_.includes(validVCIdentifiers, identifier)) { - throw new Error(`${identifier} is not valid`); - } - return true; - default: - return false; - } +function isValidGlobalIdentifier(_x) { + return _isValidGlobalIdentifier.apply(this, arguments); +} + +function _isValidGlobalIdentifier() { + _isValidGlobalIdentifier = _asyncToGenerator(function* (identifier) { + // Load the schema and it's references from a source to be used for validation and defining the schema definitions + yield schemaLoader.loadSchemaFromTitle(identifier); + + const splited = _.split(identifier, '-'); + + if (splited.length !== 3) { + throw new Error('Malformed Global Identifier'); + } + + if (!_.includes(validPrefixes, splited[0])) { + throw new Error('Invalid Global Identifier Prefix'); + } + + switch (splited[0]) { + case 'claim': + if (!_.includes(validUCAIdentifiers, splited[1]) && !_.includes(validClaimIdentifiers, identifier)) { + throw new Error(`${identifier} is not valid`); + } + + return true; + + case 'credential': + if (!_.includes(validVCIdentifiers, splited[1]) && !_.includes(validVCIdentifiers, identifier)) { + throw new Error(`${identifier} is not valid`); + } + + return true; + + default: + return false; + } + }); + return _isValidGlobalIdentifier.apply(this, arguments); } module.exports = isValidGlobalIdentifier; \ No newline at end of file diff --git a/dist/cjs/lib/crypto.js b/dist/cjs/lib/crypto.js index 2269ff8f..ec9d57be 100644 --- a/dist/cjs/lib/crypto.js +++ b/dist/cjs/lib/crypto.js @@ -1,4 +1,4 @@ -'use strict'; +"use strict"; const sjcl = require('sjcl'); diff --git a/dist/cjs/lib/did.js b/dist/cjs/lib/did.js new file mode 100644 index 00000000..6cbe2e3e --- /dev/null +++ b/dist/cjs/lib/did.js @@ -0,0 +1,84 @@ +"use strict"; + +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); }); }; } + +const { + findVerificationMethod, + CachedResolver +} = require('@digitalbazaar/did-io'); + +const didSol = require('@identity.com/did-io-driver-sol').default; + +const resolver = new CachedResolver(); // no payer needed as we are only resolving documents + +resolver.use(didSol.driver({ + payer: null +})); +module.exports = { + /** + * Checks if a verificationMethod can sign for the DID document + * + * @param didOrDocument A DID (string) or DID document (object) + * @param verificationMethod The verification method to check + * @returns {Promise} True if the verification method can sign + */ + canSign(didOrDocument, verificationMethod) { + var _this = this; + + return _asyncToGenerator(function* () { + const [verificationMethodDid] = verificationMethod.split('#'); + const document = didOrDocument.id ? didOrDocument : yield _this.resolve(didOrDocument); + const did = document.id; // if the verificationMethod DID is for the document DID + + if (verificationMethodDid === did) { + return _this.findVerificationMethod(document, verificationMethod) !== null; + } + + if (!document.controller.includes(verificationMethodDid)) { + // If the verification method DID is not a controller of the provided DID + return false; + } // Check if the verificationMethod exists on the controller DID document + + + const controllerDocument = yield _this.resolve(verificationMethodDid); + return _this.findVerificationMethod(controllerDocument, verificationMethod) !== null; + })(); + }, + + /** + * Resolves a DID document + * + * @param did The DID to resolve the document for + */ + resolve(did) { + return _asyncToGenerator(function* () { + return resolver.get({ + did + }); + })(); + }, + + /** + * Finds the verificationMethod in a document + * + * @param document The document to search through + * @param verificationMethod The verification method to return + */ + findVerificationMethod(document, verificationMethod) { + if (document.keyAgreement && document.keyAgreement.length > 0) { + return document.keyAgreement.find(agreement => agreement.id === verificationMethod); + } + + if (!document.capabilityInvocation.includes(verificationMethod)) { + return null; + } + + return findVerificationMethod({ + doc: document, + methodId: verificationMethod + }); + } + +}; \ No newline at end of file diff --git a/dist/cjs/lib/signerVerifier.js b/dist/cjs/lib/signerVerifier.js new file mode 100644 index 00000000..40b6a6df --- /dev/null +++ b/dist/cjs/lib/signerVerifier.js @@ -0,0 +1,141 @@ +"use strict"; + +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); }); }; } + +const nacl = require('tweetnacl'); + +const bs58 = require('bs58'); + +const didUtil = require('./did'); + +class Ed25519Signer { + constructor(key, verificationMethod) { + this.key = key; + this.verificationMethod = verificationMethod; + } + + sign(proof) { + const signed = nacl.sign.detached(Buffer.from(proof.merkleRoot, 'hex'), bs58.decode(this.key)); + const signature = Buffer.from(signed).toString('hex'); + return { + signature, + verificationMethod: this.verificationMethod + }; + } + +} + +class Ed25519Verifier { + constructor(key) { + this.key = key; + } + + verify(vc) { + return nacl.sign.detached.verify(Buffer.from(vc.proof.merkleRoot, 'hex'), Uint8Array.from(Buffer.from(vc.proof.merkleRootSignature.signature, 'hex')), bs58.decode(this.key)); + } + +} +/** + * Creates a signer from the provided information + * + * @param options Signer options: + * @param options.verificationMethod The verificationMethod for the signing key + * @param options.keypair The keypair to sign with + * or + * @param options.privateKey The private key to sign with + * or + * @param options.signer An object implementing a `sign(CvcMerkleProof)` method + */ + + +const signer = /*#__PURE__*/function () { + var _ref = _asyncToGenerator(function* (options) { + if (!options.signer && !options.keypair && !options.privateKey) { + throw new Error('Either a signer, keypair or privateKey is required'); + } + + const { + verificationMethod + } = options; + let { + signer: signerImpl + } = options; // Create a signer from keypair/key + + if (signerImpl) return signerImpl; + const [did] = verificationMethod.split('#'); + const document = yield didUtil.resolve(did); + let { + privateKey + } = options; + + if (!privateKey) { + privateKey = bs58.encode(options.keypair.secretKey); + } + + const foundMethod = didUtil.findVerificationMethod(document, verificationMethod); + + if (!foundMethod) { + throw new Error('The provided verificationMethod is not valid on the DID document'); + } // Check the type is supported and assign the appropriate signer + + + switch (foundMethod.type) { + case 'Ed25519VerificationKey2018': + case 'Ed25519VerificationKey2020': + signerImpl = new Ed25519Signer(privateKey, verificationMethod); + break; + + default: + throw new Error(`Unsupported type ${foundMethod.type}`); + } + + return signerImpl; + }); + + return function signer(_x) { + return _ref.apply(this, arguments); + }; +}(); +/** + * Creates a verifier based on the information provided + * @param did The issuer DID + * @param verificationMethod The verification method used to lookup the key + */ + + +const verifier = /*#__PURE__*/function () { + var _ref2 = _asyncToGenerator(function* (did, verificationMethod) { + const canSignFor = yield didUtil.canSign(did, verificationMethod); + + if (!canSignFor) { + // always return false + return { + verify: () => false + }; + } + + const [vmDid] = verificationMethod.split('#'); + const document = yield didUtil.resolve(vmDid); + const foundMethod = didUtil.findVerificationMethod(document, verificationMethod); // Check the type is supported and assign the appropriate verifier + + switch (foundMethod.type) { + case 'Ed25519VerificationKey2018': + case 'Ed25519VerificationKey2020': + return new Ed25519Verifier(foundMethod.publicKeyBase58); + + default: + throw new Error(`Unsupported type ${foundMethod.type}`); + } + }); + + return function verifier(_x2, _x3) { + return _ref2.apply(this, arguments); + }; +}(); + +module.exports = { + signer, + verifier +}; \ No newline at end of file diff --git a/dist/cjs/lib/stringUtils.js b/dist/cjs/lib/stringUtils.js new file mode 100644 index 00000000..44415b52 --- /dev/null +++ b/dist/cjs/lib/stringUtils.js @@ -0,0 +1,19 @@ +"use strict"; + +/** + * Convert strings like "SocialSecurity" to "socialSecurity". + * If passed a non-PascalCase string such as SOCIAL_SECURITY, it cannot detect that the string + * is not PascalCase and will therefore convert it to sOCIAL_SECURITY + * @param string + * @return {*} + */ +const pascalToCamelCase = string => string.replace(/^([A-Z])/, match => match.toLowerCase()); + +const identifierPattern = /(claim|credential|uca|type)-((\w+):[\w.:]+)-v(\d+)/; + +const parseIdentifier = identifier => identifier.match(identifierPattern); + +module.exports = { + pascalToCamelCase, + parseIdentifier +}; \ No newline at end of file diff --git a/dist/cjs/logger.js b/dist/cjs/logger.js index a65dd94e..41b00ecd 100644 --- a/dist/cjs/logger.js +++ b/dist/cjs/logger.js @@ -1,13 +1,27 @@ -'use strict'; +"use strict"; -const { createLogger, format, transports } = require('winston'); +module.exports = { + // eslint-disable-next-line no-unused-vars + warn(a, b = undefined) { + // eslint-disable-next-line no-console + console.warn(a); + }, -// Configure the Winston logger. For the complete documentation see https://github.com/winstonjs/winston -const logger = createLogger({ - // To see more detailed errors, change this to 'debug' - level: 'info', - format: format.combine(format.splat(), format.simple()), - transports: [new transports.Console()] -}); + // eslint-disable-next-line no-unused-vars + info(a, b = undefined) { + // eslint-disable-next-line no-console + console.info(a); + }, -module.exports = logger; \ No newline at end of file + // eslint-disable-next-line no-unused-vars + debug(a, b = undefined) {// eslint-disable-next-line no-console + // console.debug(a); // disabled + }, + + // eslint-disable-next-line no-unused-vars + error(a, b = undefined) { + // eslint-disable-next-line no-console + console.error(a); + } + +}; \ No newline at end of file diff --git a/dist/cjs/schemas/generator/SchemaGenerator.js b/dist/cjs/schemas/generator/SchemaGenerator.js deleted file mode 100644 index d9c29b71..00000000 --- a/dist/cjs/schemas/generator/SchemaGenerator.js +++ /dev/null @@ -1,257 +0,0 @@ -'use strict'; - -/* eslint-disable no-use-before-define */ -const _ = require('lodash'); -const randomString = require('randomstring'); -const Type = require('type-of-is'); -const RandExp = require('randexp'); -const { UserCollectableAttribute: UCA, definitions: ucaDefinitions } = require('@identity.com/uca'); -const { Claim, definitions, getBaseIdentifiers } = require('../../claim/Claim'); - -const DRAFT = 'http://json-schema.org/draft-07/schema#'; - -const getPropertyNameFromDefinition = definition => getBaseIdentifiers(definition.identifier).identifierComponents[2]; - -const getPropertyType = value => Type.string(value).toLowerCase(); - -const processObject = (object, outputParam, parentKey) => { - const output = outputParam || {}; - const objectProperties = object; - const { definition } = objectProperties; - delete objectProperties.definition; - - output.type = getPropertyType(objectProperties); - output.properties = output.properties || {}; - const keys = Object.entries(objectProperties); - - // too much debate on this eslint - // https://github.com/airbnb/javascript/issues/1122 - // eslint-disable-next-line no-restricted-syntax - for (const [key, value] of keys) { - // we have to get the required array from the definitions properties - - const type = getPropertyType(value); - if (type === 'object') { - output.properties[key] = processObject(value, output.properties[key], `${parentKey}.${key}`); - } else if (type === 'array') { - // recursion - // eslint-disable-next-line - output.properties[key] = processArray(value, output.properties[key], key); - } else { - output.properties[key] = {}; - output.properties[key].type = type === 'null' ? ['null', 'string'] : type; - if (definition && definition.type.properties) { - let propType = definition.type.properties.find(prop => prop.name === key); - // simple composite, one depth level civ:Identity.name for example - if (propType && propType.type.includes(':')) { - propType = definitions.find(def => def.identifier === propType.type); - } - - output.properties[key] = addMinimumMaximum(propType, output.properties[key]); - } else { - output.properties[key] = addMinimumMaximum(definition, output.properties[key]); - } - } - } - // it must be 4 here, we start the json of the VC with root - // then it's claim, then all standardize Claim are type:name - if (parentKey.includes('claim') && parentKey.split('.').length === 4) { - // with the json key of the claim - const baseUcaName = parentKey.substring('root.claim.'.length); - let typeName = (baseUcaName.substring(0, 1).toUpperCase() + baseUcaName.substring(1)).replace('.', ':'); - // regenerate uca - let refDefinition = definitions.find(def => def.identifier.includes(typeName)); - if (refDefinition == null) { - const baseName = baseUcaName.substring(0, 1).toUpperCase() + baseUcaName.substring(1); - typeName = `claim-cvc:${baseName}-v1`; - refDefinition = definitions.find(def => def.identifier.includes(typeName)); - } - if (refDefinition == null) { - typeName = `claim-cvc:${baseUcaName}-v1`; - refDefinition = definitions.find(def => def.identifier.includes(typeName)); - } - // get it's required definitions - output.required = refDefinition.type.required; - } - output.additionalProperties = false; - return output; -}; - -const processArray = (array, outputParam) => { - const output = outputParam || {}; - output.type = getPropertyType(array); - output.items = output.items || {}; - const type = getPropertyType(array[0]); - output.items.type = type; - output.additionalProperties = false; - return output; -}; - -/** - * Entry point of this class. Use this to generate an sample json data - * then an json schema from that data. That way you do not need to - * create sample or mocks json from Credentials - * - * @param definition Claim/VC definition - * @param json generated json - * @returns {{$schema: string}} expected json schema to validate this data - */ -const process = (definition, json) => { - const object = json; - const title = definition.identifier; - let processOutput; - const output = { - $schema: DRAFT - }; - output.title = title; - // Set initial object type - output.type = Type.string(object).toLowerCase(); - - // Process object - if (output.type === 'object') { - processOutput = processObject(object, {}, 'root'); - output.type = processOutput.type; - output.properties = processOutput.properties; - } - - // never allow additionalProperties - output.additionalProperties = false; - // Output - return output; -}; - -/** - * Build a sample json from an definition identifier - * Recursively make the Claim from nested properties and Claim references - * - * @param definition receive an Claim and build an sample json from it's properties - * @returns {{$schema: string}} - */ -const buildSampleJson = (definition, includeDefinitions = false) => { - let output = {}; - output = makeJsonRecursion(definition, includeDefinitions); - return output; -}; - -/** - * Recursion to build the schema from an json value - * @param ucaDefinition - */ -const makeJsonRecursion = (ucaDefinition, includeDefinitions = false) => { - let output = {}; - const typeName = Claim.getTypeName(ucaDefinition); - if (typeof ucaDefinition.type === 'object' && ucaDefinition.type.properties !== undefined) { - // array of properties - ucaDefinition.type.properties.forEach(property => { - output[property.name] = generateRandomValueForType(property, includeDefinitions); - }); - } else if (typeName === 'Array') { - const itemType = ucaDefinition.items.type; - const itemDefinition = _.find(definitions, { identifier: itemType }); - output = [makeJsonRecursion(itemDefinition, includeDefinitions)]; - } else if (typeName !== 'Object') { - // not a reference - const propertyName = getPropertyNameFromDefinition(ucaDefinition); - if (typeof ucaDefinition.pattern !== 'undefined' && ucaDefinition.pattern !== null) { - output[propertyName] = new RandExp(ucaDefinition.pattern).gen(); - } else { - output[propertyName] = generateRandomValueForType(ucaDefinition, includeDefinitions); - } - } else { - // a direct reference to a composite type - output = generateRandomValueForType(ucaDefinition, includeDefinitions); - } - if (includeDefinitions && output.definition == null) { - output.definition = ucaDefinition; - } - return output; -}; - -/** - * This method is an auxiliary method to allow random values to easy create - * json schemas from JSON values generated from Claim/VC - * - * @param definition - * @returns {number} - */ -const generateRandomNumberValueWithRange = definition => { - let genRandomNumber = Math.random() * 100; - - if (definition !== null) { - if (typeof definition.minimum !== 'undefined' && definition.minimum !== null && genRandomNumber < definition.minimum) { - genRandomNumber = definition.minimum; - } - - if (definition.exclusiveMinimum !== 'undefined' && definition.exclusiveMinimum !== null && genRandomNumber <= definition.exclusiveMinimum) { - genRandomNumber = definition.exclusiveMinimum + 0.1; - } - - if (typeof definition.maximum !== 'undefined' && definition.maximum !== null && genRandomNumber > definition.maximum) { - genRandomNumber = definition.maximum; - } - - if (definition.exclusiveMaximum !== 'undefined' && definition.exclusiveMaximum !== null && genRandomNumber >= definition.exclusiveMaximum) { - genRandomNumber = definition.exclusiveMaximum - 0.1; - } - } - - return genRandomNumber; -}; - -const generateRandomValueForType = (definition, includeDefinitions = false) => { - const typeName = definition.type; - let refDefinition = definition; - let resolvedTypeName = typeName; - if (typeName.includes(':')) { - // simple composite, one depth level civ:Identity.name for example - refDefinition = definitions.find(def => def.identifier === typeName); - if (refDefinition != null) { - resolvedTypeName = Claim.resolveType(refDefinition); - } else { - refDefinition = ucaDefinitions.find(def => def.identifier === typeName); - if (refDefinition) { - resolvedTypeName = UCA.resolveType(refDefinition); - } - } - } - // generate sample data - // that's why the magic numbers are here - switch (resolvedTypeName) { - case 'String': - if (refDefinition.enum) { - return _.values(refDefinition.enum)[0]; - } - return refDefinition && refDefinition.pattern ? new RandExp(refDefinition.pattern).gen() : randomString.generate(10); - case 'Number': - return generateRandomNumberValueWithRange(refDefinition); - case 'Boolean': - return Math.round(Math.random()) === 1; - default: - return makeJsonRecursion(refDefinition, includeDefinitions); - } -}; - -const addMinimumMaximum = (definition, object) => { - const output = object; - // for simple Claim get json schema properties - if (typeof definition !== 'undefined' && definition !== null) { - if (definition.exclusiveMinimum != null) { - output.exclusiveMinimum = definition.exclusiveMinimum; - } - - if (definition.minimum != null) { - output.minimum = definition.minimum; - } - - if (definition.exclusiveMaximum != null) { - output.exclusiveMaximum = definition.exclusiveMaximum; - } - - if (definition.maximum != null) { - output.maximum = definition.maximum; - } - } - return output; -}; - -module.exports = { process, buildSampleJson }; \ No newline at end of file diff --git a/dist/cjs/schemas/jsonSchema/index.js b/dist/cjs/schemas/jsonSchema/index.js new file mode 100644 index 00000000..6674910c --- /dev/null +++ b/dist/cjs/schemas/jsonSchema/index.js @@ -0,0 +1,649 @@ +"use strict"; + +function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); 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 = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : 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; } + +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); }); }; } + +const _ = require('lodash'); + +const Ajv = require('ajv').default; + +const traverse = require('json-schema-traverse'); + +const { + definitions: ucaDefinitions +} = require('@identity.com/uca'); + +const addFormats = require('ajv-formats').default; + +const definitions = require('../../claim/definitions'); + +const credentialDefinitions = require('../../creds/definitions'); + +let summaryMap = {}; +/** + * Code generated to create a sumary map for a human readable output. + */ + +class SummaryMapper { + static addDefinition(def) { + const textLabel = SummaryMapper.getTextLabel(def.identifier); + + if (textLabel) { + const mapItem = _.get(summaryMap, textLabel); + + if (mapItem) { + mapItem.labelFor.push(def.identifier); + } else { + summaryMap[textLabel] = { + identifier: def.identifier, + textLabel, + credentials: SummaryMapper.getCredentials(def.identifier), + labelFor: [def.identifier], + changeable: SummaryMapper.isUpdatable(textLabel), + claimPath: SummaryMapper.getClaimPath(def.identifier) + }; + } + } + } + + static addCredentialDefinition(def) { + const textLabel = SummaryMapper.getTextLabel(def.identifier); + + if (textLabel) { + summaryMap[textLabel] = { + identifier: def.identifier, + textLabel, + credentials: [def.identifier], + labelFor: [def.identifier], + changeable: SummaryMapper.isUpdatable(textLabel), + claimPath: null + }; + } + } + + static getTextLabel(identifier) { + // eslint-disable-next-line no-unused-vars + const [type, name, version] = _.split(identifier, '-'); // eslint-disable-next-line no-unused-vars + + + const [namespace, unique, path] = _.split(name, ':'); + + if (type && unique) { + return `${unique}.${type}${path ? `.${path}` : ''}`.toLowerCase(); + } + + return null; + } + + static isUpdatable(textLabel) { + const notUpdatable = ['document.placeofbirth.claim', 'document.dateofbirth.claim']; + return !_.includes(notUpdatable, textLabel); + } + + static getCredentials(identifier) { + // eslint-disable-next-line no-unused-vars + const [type, name, version] = _.split(identifier, '-'); + + if (type === 'credential') { + return [identifier]; + } + + const credentials = _.filter(credentialDefinitions, item => _.includes(item.depends, identifier)); + + return _.map(credentials, item => item.identifier); + } + + static getClaimPath(identifier) { + // eslint-disable-next-line no-unused-vars + const [type, name, version] = _.split(identifier, '-'); + + if (type === 'credential') { + return null; + } + + return SummaryMapper.getPath(identifier); + } + + static getBaseIdentifiers(identifier) { + const claimRegex = /claim-cvc:(.*)\.(.*)-v\d*/; + let isNewIdentifier = true; + let identifierComponents = claimRegex.exec(identifier); + + if (identifierComponents === null) { + identifierComponents = _.split(identifier, ':'); + isNewIdentifier = false; + } + + return { + identifierComponents, + isNewIdentifier + }; + } + + static getPath(identifier) { + const { + identifierComponents + } = SummaryMapper.getBaseIdentifiers(identifier); + + const baseName = _.camelCase(identifierComponents[1]); + + return baseName !== 'type' ? `${baseName}.${identifierComponents[2]}` : identifierComponents[2]; + } + +} + +const getSchemaVersion = identifier => { + const matches = identifier.match(/-v([\d]+$)/); + + if (matches && matches.length > 1) { + return matches[1]; + } + + return '1'; +}; + +function transformUcaIdToClaimId(identifier) { + const identifierComponents = identifier.split(':'); + return `claim-cvc:${identifierComponents[1]}.${identifierComponents[2]}-v1`; +} + +function isDefinitionEqual(definition, ucaDefinition) { + return definition.identifier === transformUcaIdToClaimId(ucaDefinition) || definition.identifier === ucaDefinition; +} + +const isUCA = uca => /^[^:]+:[^:]+:[^:]+$/.test(uca); +/** + * This class loads the schema definitions as needed by using loaders provided by the + */ + + +class SchemaLoader { + constructor() { + this.loaders = []; + this.definitions = definitions; + this.ucaDefinitions = ucaDefinitions; + this.credentialDefinitions = credentialDefinitions; + this.summaryMap = summaryMap; + this.validIdentifiers = []; + this.validUcaIdentifiers = []; + this.validCredentialIdentifiers = []; + this.ucaCompared = []; // allowUnionTypes is required because of the anchor in the proof can be a string/object (this should be changed) + + this.ajv = new Ajv({ + logger: console, + allErrors: true, + verbose: true, + strict: true, + allowUnionTypes: true + }); // add data formats such as date-time + + addFormats(this.ajv); + this.ajv.addKeyword('attestable'); // Needed to add these to support "reversing" definitions back to the previous definitions for backwards + // compatibilty. These should be removed? + + this.ajv.addKeyword('transient'); + this.ajv.addKeyword('credentialItem'); + this.ajv.addKeyword('alias'); + this.ajv.addKeyword('deambiguify'); + } + + reset() { + this.ucaDefinitions.length = 0; + this.definitions.length = 0; + this.credentialDefinitions.length = 0; + this.validIdentifiers.length = 0; + this.validCredentialIdentifiers.length = 0; + this.validUcaIdentifiers.length = 0; + this.ajv.removeSchema(/.*/); + summaryMap = {}; + this.summaryMap = summaryMap; + } + /** + * Adds a schema loader which references where the schemas are loaded from + */ + + + addLoader(loader) { + this.loaders.push(loader); + } + + loadSchemaFromUri(uri) { + var _this = this; + + return _asyncToGenerator(function* () { + const title = uri.split('#')[0].match('[^/]+$', uri); + const schema = yield _this.loadSchemaFromTitle(title[0]); + return schema; + })(); + } + + loadPropertySchema(schema, definition, ref, property) { + var _this2 = this; + + return _asyncToGenerator(function* () { + const propertySchema = yield _this2.loadSchemaFromUri(ref); + + if (propertySchema !== null) { + definition.depends.push(propertySchema.title); + } + + const csProperties = yield _this2.getCredentialSubjectProperties(schema); + + if (csProperties.required && csProperties.required.includes(property)) { + definition.required.push(propertySchema.title); + } + })(); + } + /** + * Supporting both claim and credentialSubject + * TODO: remove this once backwards compatibility has been removed + * @param schema + * @returns {*|(() => Promise)} + */ + + + getCredentialSubjectProperties(schema) { + var _this3 = this; + + return _asyncToGenerator(function* () { + const schemaProperties = yield _this3.flattenCredentialSchemaProperties(schema); + return schemaProperties.credentialSubject ? schemaProperties.credentialSubject : schemaProperties.claim; + })(); + } + /** + * Flattens the properties of a schema if there are any referenced schemas + * @param schema + * @returns {Promise<*>} + */ + + + flattenCredentialSchemaProperties(schema) { + var _this4 = this; + + return _asyncToGenerator(function* () { + let properties = schema.properties ? schema.properties : {}; + + if (schema.allOf) { + const promises = schema.allOf.map( /*#__PURE__*/function () { + var _ref = _asyncToGenerator(function* (allOf) { + if (allOf.$ref) { + const refSchema = yield _this4.loadSchemaFromUri(allOf.$ref); + const refProperties = yield _this4.flattenCredentialSchemaProperties(refSchema); + properties = _objectSpread(_objectSpread({}, properties), refProperties); + } + + if (allOf.properties) { + properties = _objectSpread(_objectSpread({}, properties), allOf.properties); + } + }); + + return function (_x) { + return _ref.apply(this, arguments); + }; + }()); + yield Promise.all(promises); + } + + return properties; + })(); + } + /** + * Adds a claim definition to be backwards compatible with the old schema structure. + */ + + + addDefinition(schema) { + var _this5 = this; + + return _asyncToGenerator(function* () { + if (/^credential-/.test(schema.title)) { + yield _this5.addCredentialDefinition(schema); + } else { + yield _this5.addClaimDefinition(schema); + } + })(); + } + /** + * Adds a credential definition to be backwards compatible with the old schema structure. + */ + + + addCredentialDefinition(schema) { + var _this6 = this; + + return _asyncToGenerator(function* () { + const definition = { + identifier: schema.title, + version: getSchemaVersion(schema.title), + depends: [] + }; + + if (schema.transient) { + definition.transient = true; + } + + const credentialSubjectDefinition = yield _this6.getCredentialSubjectProperties(schema); + + if (credentialSubjectDefinition.required) { + definition.required = []; + } + + const references = []; + + _.forEach(credentialSubjectDefinition.properties, vo => { + _.forEach(vo.properties, (vi, ki) => { + references.push({ + ref: vo.properties[ki].$ref, + property: ki + }); + }); + }); + + yield _.reduce(references, /*#__PURE__*/function () { + var _ref2 = _asyncToGenerator(function* (promise, value) { + yield promise; + return _this6.loadPropertySchema(schema, definition, value.ref, value.property); + }); + + return function (_x2, _x3) { + return _ref2.apply(this, arguments); + }; + }(), Promise.resolve()); + + _this6.credentialDefinitions.push(definition); + + _this6.validCredentialIdentifiers.push(definition.identifier); + + SummaryMapper.addCredentialDefinition(definition); + })(); + } + + shouldAddClaimDefinition(schema) { + var _this7 = this; + + return _asyncToGenerator(function* () { + if (isUCA(schema.title)) { + const transformed = transformUcaIdToClaimId(schema.title); + + if (!_this7.ucaCompared.includes(schema.title)) { + yield _this7.loadSchemaFromTitle(transformed); + } + + _this7.ucaCompared.push(schema.title); + + let found = false; + + _this7.definitions.some(definition => { + if (isDefinitionEqual(definition, schema.title)) { + found = true; + } + + return found; + }); + + if (found) { + return false; + } + } + + return true; + })(); + } + + addClaimDefinition(schema) { + var _this8 = this; + + return _asyncToGenerator(function* () { + const definition = { + identifier: schema.title, + version: getSchemaVersion(schema.title), + type: yield _this8.findDefinitionType(schema) + }; + + if (definition.type === 'Array') { + const subSchema = yield _this8.loadSchemaFromUri(schema.items.$ref); + definition.items = { + type: subSchema.title + }; + } + + ['attestable', 'credentialItem', 'minimum', 'maximum', 'alias', 'description'].forEach(property => { + if (property in schema) { + definition[property] = schema[property]; + } + }); + + if (schema.pattern) { + // definition.pattern = new RegExp(schema.pattern.substring(1, schema.pattern.length - 1)); + definition.pattern = new RegExp(schema.pattern); + } + + if (schema.required) { + definition.type.required = schema.required; + } + + if (schema.enum) { + definition.enum = {}; + + _.forEach(schema.enum, value => { + definition.enum[value.toUpperCase()] = value; + }); + } + + if (yield _this8.shouldAddClaimDefinition(schema)) { + _this8.definitions.push(definition); + + _this8.validIdentifiers.push(schema.title); + } + + if (isUCA(schema.title)) { + _this8.ucaDefinitions.push(definition); + + _this8.validUcaIdentifiers.push(schema.title); + } + + SummaryMapper.addDefinition(definition); + })(); + } + + getPropertyValue(defProperties, property, name) { + var _this9 = this; + + return _asyncToGenerator(function* () { + const { + deambiguify, + items + } = property; + let { + type + } = property; + + if (type === 'array' || items && items.$ref) { + if (items.$ref) { + const arraySchema = yield _this9.loadSchemaFromUri(items.$ref); + type = arraySchema.title; + } else { + type = _.capitalize(type); + } + } + + if (property.allOf) { + const schema = yield _this9.loadSchemaFromUri(property.allOf[0].$ref); + type = schema.title; + } + + const defProperty = { + name, + type + }; + + if (deambiguify) { + defProperty.deambiguify = deambiguify; + } + + defProperties.push(defProperty); + })(); + } + + getPropertyValues(properties) { + var _this10 = this; + + return _asyncToGenerator(function* () { + const defProperties = []; + yield _.reduce(properties, /*#__PURE__*/function () { + var _ref3 = _asyncToGenerator(function* (promise, value, name) { + yield promise; + return _this10.getPropertyValue(defProperties, value, name); + }); + + return function (_x4, _x5, _x6) { + return _ref3.apply(this, arguments); + }; + }(), Promise.resolve()); + return { + properties: defProperties + }; + })(); + } + /** + * Finds the definition/properties of a schema + */ + + + findDefinitionType(schema) { + var _this11 = this; + + return _asyncToGenerator(function* () { + if (schema.allOf) { + const subSchema = yield _this11.loadSchemaFromUri(schema.allOf[0].$ref); + + if (subSchema == null) { + return null; + } + + return subSchema.title; + } + + if (schema.type === 'object') { + if (!_.isEmpty(schema.properties)) { + return _this11.getPropertyValues(schema.properties); + } + } + + if (schema.type === 'array') { + return 'Array'; + } + + return _.capitalize(schema.type); + })(); + } + /** + * Loads a schema, traversing all the subschemas and loading them as well + */ + + + loadSchemaFromTitle(title) { + var _this12 = this; + + return _asyncToGenerator(function* () { + const loader = _this12.findSchemaLoader(title); + + if (loader == null) { + return null; + } + + const schemaId = loader.schemaId(title); + + const existingSchema = _this12.ajv.getSchema(schemaId); + + let schema; // If AJV is unaware of the schema, look it up and create it + + if (!existingSchema) { + schema = yield loader.loadSchema(title); + + if (schema === null) { + return null; + } // Loads all referenced schemas + + + const references = []; + traverse(schema, { + cb: currentNode => { + if (currentNode.$ref !== undefined && !currentNode.$ref.startsWith('#')) { + // Prevent the same schema loaded multiple times + references.push(_this12.loadSchemaFromUri(currentNode.$ref)); + } + } + }); + yield Promise.all(references); + yield _this12.addDefinition(schema); + + try { + _this12.ajv.addSchema(schema); + } catch (e) {// This could only happen if we have a cyclic dependency, or the same ref multiple times in the schema... + } + + return schema; + } + + return existingSchema.schema; + })(); + } + /** + * Finds the correct schema loader based on the identifier + */ + + + findSchemaLoader(identifier) { + return _.find(this.loaders, loader => loader.valid(identifier)); + } + /** + * Validates the schema based on identifier and supplied data. + */ + + + validateSchema(identifier, data) { + var _this13 = this; + + return _asyncToGenerator(function* () { + const loader = _this13.findSchemaLoader(identifier); + + yield _this13.loadSchemaFromTitle(identifier); + + _this13.validate(loader.schemaId(identifier), data); + })(); + } + + validate(schemaRef, value) { + const validateSchema = this.ajv.getSchema(schemaRef); + + if (typeof validateSchema === 'undefined') { + throw new Error(`Invalid schema id: ${schemaRef}`); + } + + const valid = validateSchema(value); + + if (!valid) { + _.forEach(validateSchema.errors, error => { + if (error.params && error.params.missingProperty) { + throw new Error(`Missing required fields to ${validateSchema.schema.title}`); + } + }); + + throw new Error(`Invalid value. Errors: ${JSON.stringify(validateSchema.errors, null, 2)}`); + } + } + +} + +const schemaLoader = new SchemaLoader(); +module.exports = { + schemaLoader +}; \ No newline at end of file diff --git a/dist/cjs/schemas/jsonSchema/loaders/cvc.js b/dist/cjs/schemas/jsonSchema/loaders/cvc.js new file mode 100644 index 00000000..72f4f5a6 --- /dev/null +++ b/dist/cjs/schemas/jsonSchema/loaders/cvc.js @@ -0,0 +1,161 @@ +"use strict"; + +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); }); }; } + +const fs = require('fs'); + +const { + parseIdentifier +} = require('../../../lib/stringUtils'); + +const { + services +} = require('../../../services'); + +const rootUri = 'http://identity.com/schemas/'; +const DEFAULT_SCHEMA_PATH = 'http://dev-schemas.civic.com.s3-website-us-east-1.amazonaws.com/dev'; + +class FSSchemaCache { + constructor(cachePath = './.tmp/schemas') { + this.cachePath = cachePath; + fs.mkdirSync(cachePath, { + recursive: true + }); + } + + get(identifier) { + const cachePath = `${this.cachePath}/${identifier}.schema.json`; + + if (!fs.existsSync(cachePath)) { + return null; + } + + return fs.readFileSync(cachePath, { + encoding: 'utf8' + }); + } + + set(identifier, schema) { + const cachePath = `${this.cachePath}/${identifier}.schema.json`; + fs.writeFileSync(cachePath, schema, { + encoding: 'utf8' + }); + } + +} + +const getIdentifierPath = identifier => { + let identifierPath; + + if (/^cvc:.*$/.test(identifier)) { + identifierPath = `uca/1/${identifier}`; + } else { + const parsedIdentifier = parseIdentifier(identifier); + + if (parsedIdentifier) { + identifierPath = `${parsedIdentifier[1]}/${parsedIdentifier[4]}/${parsedIdentifier[2]}`; + } + } + + return identifierPath; +}; +/** + * This is a sample schema loader, to be used for testing or civic.com claims & credential implementations + */ + + +class CVCLoader { + constructor(http = services.container.Http, cache = new FSSchemaCache(), schemaPath = DEFAULT_SCHEMA_PATH) { + this.http = http; + this.cache = cache; + this.schemaPath = schemaPath; + } + /** + * Gets the schema id based on the identifier + */ + // eslint-disable-next-line class-methods-use-this + + + schemaId(identifier) { + return rootUri + identifier; + } + /** + * Tests to see if this loader is valid for the supplied identifier + */ + // eslint-disable-next-line class-methods-use-this + + + valid(identifier) { + return /^(claim|credential|type)-(cvc|alt):.*$/.test(identifier) || /^cvc:.*$/.test(identifier); + } + /** + * Loads the schema based on the identifier + */ + + + loadSchema(identifier) { + var _this = this; + + return _asyncToGenerator(function* () { + let schema = null; + + if (_this.cache) { + schema = _this.cache.get(identifier); + } // Only load the schema remotely if a base url was provided and none was found locally + + + if (!schema) { + schema = yield _this.remote(identifier); + + if (_this.cache && schema) { + _this.cache.set(identifier, schema); + } + } + + try { + return !schema ? null : JSON.parse(schema); + } catch (e) { + return null; + } + })(); + } + /** + * Loads a schema from a remote location + * @param identifier The identifer to load the schema for + * @returns The schema object if found + */ + + + remote(identifier) { + var _this2 = this; + + return _asyncToGenerator(function* () { + const identifierPath = getIdentifierPath(identifier); + + if (!identifierPath) { + return null; + } + + const uri = `${_this2.schemaPath}/${identifierPath}.schema.json`; + let response = null; + + try { + response = yield _this2.http.request(uri); + } catch (e) { + // If it fails due to timeout/connectivity, or a server side issue - try again + if (!e.statusCode || e.statusCode >= 500 && e.statusCode <= 599) { + response = yield services.container.Http.request(uri); + } else if (e.statusCode < 400 || e.statusCode >= 500) { + throw e; + } + } + + return response; + })(); + } + +} + +module.exports = CVCLoader; \ No newline at end of file diff --git a/dist/cjs/services/DefaultAnchorServiceImpl.js b/dist/cjs/services/DefaultAnchorServiceImpl.js index c57db253..67fa787c 100644 --- a/dist/cjs/services/DefaultAnchorServiceImpl.js +++ b/dist/cjs/services/DefaultAnchorServiceImpl.js @@ -1,28 +1,38 @@ -'use strict'; +"use strict"; -function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } +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); }); }; } /** * Current Anchor/Attester service * */ const uuid = require('uuid/v4'); -const { HDNode, ECSignature } = require('bitcoinjs-lib'); + +const { + HDNode, + ECSignature +} = require('bitcoinjs-lib'); + const sjcl = require('sjcl'); -const logger = require('../logger'); +const logger = require('../logger'); /** * An Anchor/Attester implementation * * @param {*} config * @param {*} http */ + + function DummyAnchorServiceImpl(config, http) { var _this = this; this.config = config; - this.http = http; - const pollService = (() => { + this.http = http; // eslint-disable-next-line no-unused-vars + + const pollService = /*#__PURE__*/function () { var _ref = _asyncToGenerator(function* (statusUrl) { try { const attestation = yield _this.http.request({ @@ -36,10 +46,12 @@ function DummyAnchorServiceImpl(config, http) { // eslint-disable-next-line no-unused-vars return yield pollService(statusUrl); } + if (attestation && attestation.type !== 'permanent') { attestation.statusUrl = statusUrl; return attestation; } + return attestation; } catch (error) { logger.error(`Error polling: ${statusUrl}`, JSON.stringify(error, null, 2)); @@ -50,88 +62,94 @@ function DummyAnchorServiceImpl(config, http) { return function pollService(_x) { return _ref.apply(this, arguments); }; - })(); - - this.anchor = (() => { - var _ref2 = _asyncToGenerator(function* (options = {}) { - return Promise.resolve({ - subject: { - pub: 'xpub:dummy', - label: options.subject && options.subject.label ? options.subject.label : null, - data: options.subject && options.subject.data ? options.subject.data : null, - signature: 'signed:dummy' - }, - walletId: 'none', - cosigners: [{ - pub: 'xpub:dummy' - }, { - pub: 'xpub:dummy' - }], - authority: { - pub: 'xpub:dummy', - path: '/' - }, - coin: 'dummycoin', - tx: uuid(), - network: 'dummynet', - type: 'temporary', - civicAsPrimary: false, - schema: 'dummy-20180201' - }); + }(); + + this.anchor = /*#__PURE__*/_asyncToGenerator(function* (options = {}) { + return Promise.resolve({ + subject: { + pub: 'xpub:dummy', + label: options.subject && options.subject.label ? options.subject.label : null, + data: options.subject && options.subject.data ? options.subject.data : null, + signature: 'signed:dummy' + }, + walletId: 'none', + cosigners: [{ + pub: 'xpub:dummy' + }, { + pub: 'xpub:dummy' + }], + authority: { + pub: 'xpub:dummy', + path: '/' + }, + coin: 'dummycoin', + tx: uuid(), + network: 'dummynet', + type: 'temporary', + civicAsPrimary: false, + schema: 'dummy-20180201' }); + }); - return function () { - return _ref2.apply(this, arguments); - }; - })(); - - this.update = (() => { + this.update = /*#__PURE__*/function () { var _ref3 = _asyncToGenerator(function* (tempAnchor) { tempAnchor.type = 'permanent'; // eslint-disable-line + tempAnchor.value = new uuid(); // eslint-disable-line + return Promise.resolve(tempAnchor); }); return function (_x2) { return _ref3.apply(this, arguments); }; - })(); + }(); this.verifySignature = (proof, pinnedPubKey) => { - const { subject } = proof.anchor; + const { + subject + } = proof.anchor; const anchorSubjectValidation = this.verifySubjectSignature(subject, pinnedPubKey); return anchorSubjectValidation && subject.data === proof.merkleRoot; }; - /** * This method checks if the subject signature matches the pub key * @param subject a json with label, data, signature, pub * @returns {*} true or false for the validation */ + + this.verifySubjectSignature = (subject, pinnedPubKey) => { // Do not use JS destruct on the next line, We need to predict the JSON order - const toHash = JSON.stringify({ xpub: subject.pub, label: subject.label, data: subject.data }); + const toHash = JSON.stringify({ + xpub: subject.pub, + label: subject.label, + data: subject.data + }); const hash = sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash(toHash)); const subjectSignature = ECSignature.fromDER(Buffer.from(subject.signature, 'hex')); return HDNode.fromBase58(pinnedPubKey || subject.pub).keyPair.verify(Buffer.from(hash, 'hex'), subjectSignature); }; - /** * This method checks that the attestation / anchor exists on the BC */ - this.verifyAttestation = (() => { + + + this.verifyAttestation = /*#__PURE__*/function () { var _ref4 = _asyncToGenerator(function* (proof, offline = true) { const path = '/proof'; const endpoint = `${_this.config.attestationService}${path}`; - const requestOptions = { url: endpoint, - body: { attestation: proof.anchor, offline }, + body: { + attestation: proof.anchor, + offline + }, method: 'POST', json: true, simple: true // reject if not 2XX - }; + }; const response = yield _this.http.request(requestOptions); return response.valid; }); @@ -139,18 +157,19 @@ function DummyAnchorServiceImpl(config, http) { return function (_x3) { return _ref4.apply(this, arguments); }; - })(); + }(); - this.revokeAttestation = (() => { + this.revokeAttestation = /*#__PURE__*/function () { var _ref5 = _asyncToGenerator(function* (signature) { signature.revoked = true; // eslint-disable-line + return Promise.resolve(signature); }); return function (_x4) { return _ref5.apply(this, arguments); }; - })(); + }(); this.isRevoked = signature => signature.revoked ? signature.revoked : false; diff --git a/dist/cjs/services/DummyAnchorServiceImpl.js b/dist/cjs/services/DummyAnchorServiceImpl.js index 70647746..c03fcae6 100644 --- a/dist/cjs/services/DummyAnchorServiceImpl.js +++ b/dist/cjs/services/DummyAnchorServiceImpl.js @@ -1,26 +1,31 @@ -'use strict'; +"use strict"; -function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } +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); }); }; } /** * Current Anchor/Attester service * */ const uuid = require('uuid/v4'); -const logger = require('../logger'); +const logger = require('../logger'); /** * An Anchor/Attester implementation * * @param {*} config * @param {*} http */ + + function DummyAnchorServiceImpl(config, http) { var _this = this; this.config = config; - this.http = http; - const pollService = (() => { + this.http = http; // eslint-disable-next-line no-unused-vars + + const pollService = /*#__PURE__*/function () { var _ref = _asyncToGenerator(function* (statusUrl) { try { const attestation = yield _this.http.request({ @@ -34,10 +39,12 @@ function DummyAnchorServiceImpl(config, http) { // eslint-disable-next-line no-unused-vars return yield pollService(statusUrl); } + if (attestation && attestation.type !== 'permanent') { attestation.statusUrl = statusUrl; return attestation; } + return attestation; } catch (error) { logger.error(`Error polling: ${statusUrl}`, JSON.stringify(error, null, 2)); @@ -48,46 +55,41 @@ function DummyAnchorServiceImpl(config, http) { return function pollService(_x) { return _ref.apply(this, arguments); }; - })(); - - this.anchor = (() => { - var _ref2 = _asyncToGenerator(function* (options = {}) { - return Promise.resolve({ - subject: { - pub: 'xpub:dummy', - label: options.subject && options.subject.label ? options.subject.label : null, - data: options.subject && options.subject.data ? options.subject.data : null, - signature: 'signed:dummy' - }, - walletId: 'none', - cosigners: [{ - pub: 'xpub:dummy' - }, { - pub: 'xpub:dummy' - }], - authority: { - pub: 'xpub:dummy', - path: '/' - }, - coin: 'dummycoin', - tx: new uuid(), // eslint-disable-line - network: 'dummynet', - type: 'temporary', - civicAsPrimary: false, - schema: 'dummy-20180201' - }); + }(); + + this.anchor = /*#__PURE__*/_asyncToGenerator(function* (options = {}) { + return Promise.resolve({ + subject: { + pub: 'xpub:dummy', + label: options.subject && options.subject.label ? options.subject.label : null, + data: options.subject && options.subject.data ? options.subject.data : null, + signature: 'signed:dummy' + }, + walletId: 'none', + cosigners: [{ + pub: 'xpub:dummy' + }, { + pub: 'xpub:dummy' + }], + authority: { + pub: 'xpub:dummy', + path: '/' + }, + coin: 'dummycoin', + tx: new uuid(), + // eslint-disable-line + network: 'dummynet', + type: 'temporary', + civicAsPrimary: false, + schema: 'dummy-20180201' }); + }); - return function () { - return _ref2.apply(this, arguments); - }; - })(); - - this.update = (() => { + this.update = /*#__PURE__*/function () { var _ref3 = _asyncToGenerator(function* (tempAnchor) { // eslint-disable-next-line no-param-reassign - tempAnchor.type = 'permanent'; - // eslint-disable-next-line no-param-reassign + tempAnchor.type = 'permanent'; // eslint-disable-next-line no-param-reassign + tempAnchor.value = uuid(); return Promise.resolve(tempAnchor); }); @@ -95,24 +97,26 @@ function DummyAnchorServiceImpl(config, http) { return function (_x2) { return _ref3.apply(this, arguments); }; - })(); + }(); this.verifySignature = () => true; - /** * This method checks if the subject signature matches the pub key * @returns {*} true or false for the validation */ - this.verifySubjectSignature = () => true; + + this.verifySubjectSignature = () => true; /** * This method checks that the attestation / anchor exists on the BC */ - this.verifyAttestation = _asyncToGenerator(function* () { + + + this.verifyAttestation = /*#__PURE__*/_asyncToGenerator(function* () { return true; }); - this.revokeAttestation = (() => { + this.revokeAttestation = /*#__PURE__*/function () { var _ref5 = _asyncToGenerator(function* (signature) { // eslint-disable-next-line no-param-reassign signature.revoked = true; @@ -122,7 +126,7 @@ function DummyAnchorServiceImpl(config, http) { return function (_x3) { return _ref5.apply(this, arguments); }; - })(); + }(); this.isRevoked = signature => signature.revoked ? signature.revoked : false; diff --git a/dist/cjs/services/MiniCryptoManagerImpl.js b/dist/cjs/services/MiniCryptoManagerImpl.js index 23f20484..958f0987 100644 --- a/dist/cjs/services/MiniCryptoManagerImpl.js +++ b/dist/cjs/services/MiniCryptoManagerImpl.js @@ -1,7 +1,9 @@ -'use strict'; - -const { HDNode, ECSignature } = require('bitcoinjs-lib'); +"use strict"; +const { + HDNode, + ECSignature +} = require('bitcoinjs-lib'); /** * MiniCryptoManagerImpl - A minimal CryptoManagerImpl for the portable CryptoManagerInterface * to provide only default sign() and verify() functions to credential-commons with minimal dependencies. @@ -16,16 +18,19 @@ const { HDNode, ECSignature } = require('bitcoinjs-lib'); * You should `installKey` a PVT key or a PUB key (verify only) before call `sign()` or `verify()`. * The installed key is removed after `sign()` or `verify()` function was called. */ + + class MiniCryptoManagerImpl { constructor() { this.KEY_STORAGE = {}; } - /** * Install a pvt or a pub key on a keyName to be used on `sign()` or `verify()` function later. * @param {} keyName - name of the key to be installed. * @param {} key - a pvt or a pub key in base58 format. */ + + installKey(keyName, key) { try { // Test if key is a valid HDNode key @@ -35,7 +40,6 @@ class MiniCryptoManagerImpl { throw new Error(`Invalid key format: ${err.message}`); } } - /** * Return input data signed using the specified key. * @@ -44,20 +48,18 @@ class MiniCryptoManagerImpl { * @param { string } keyName - name of the key to be used to sign. * @param { string } hexHash - hex string representation of the hash */ + + sign(keyName, hexHash) { const privateKey = this.KEY_STORAGE[keyName]; const keyPair = HDNode.fromBase58(privateKey); - const hash = Buffer.from(hexHash, 'hex'); const signature = keyPair.sign(hash); - const hexSignature = signature.toDER().toString('hex'); + const hexSignature = signature.toDER().toString('hex'); // keys are volatile in this impl, removes - // keys are volatile in this impl, removes delete this.KEY_STORAGE[keyName]; - return hexSignature; } - /** * Return true if signature has been verified, false otherwise. * @@ -65,19 +67,19 @@ class MiniCryptoManagerImpl { * @param { string } hexHash - hex string representation of the hash * @param { string } hexSignature - DER encoded signature. */ + + verify(keyName, hexHash, hexSignature) { const key = this.KEY_STORAGE[keyName]; const keyPair = HDNode.fromBase58(key); - const hash = Buffer.from(hexHash, 'hex'); const signature = Buffer.from(hexSignature, 'hex'); - const ecSignature = ECSignature.fromDER(signature); + const ecSignature = ECSignature.fromDER(signature); // keys are volatile in this impl, removes - // keys are volatile in this impl, removes delete this.KEY_STORAGE[keyName]; - return keyPair.verify(hash, ecSignature); } + } module.exports = MiniCryptoManagerImpl; \ No newline at end of file diff --git a/dist/cjs/services/__mocks__/httpService.js b/dist/cjs/services/__mocks__/httpService.js index 0a616eb4..ac821d70 100644 --- a/dist/cjs/services/__mocks__/httpService.js +++ b/dist/cjs/services/__mocks__/httpService.js @@ -1,19 +1,29 @@ -'use strict'; +"use strict"; -function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } +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); } } -/* eslint-disable max-len */ +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); }); }; } +/* eslint-disable max-len */ const _ = require('lodash'); + const logger = require('../../logger'); function HttpServiceConstructor() { this.name = 'mockHttp'; - this.request = (() => { + + this.request = /*#__PURE__*/function () { var _ref = _asyncToGenerator(function* (uri, options) { - logger.debug(`Mocking request for: ${JSON.stringify({ uri, options }, null, 2)}`); - const params = _.isString(uri) ? { url: uri } : uri; + logger.debug(`Mocking request for: ${JSON.stringify({ + uri, + options + }, null, 2)}`); + const params = _.isString(uri) ? { + url: uri + } : uri; + _.assign(params, options); + const responses = [{ path: '/registry', response: { @@ -24,13 +34,19 @@ function HttpServiceConstructor() { } }, { path: '/jwt', - response: { jwt: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJqdGkiOiIyYzdlNjQ4YS1hNDhmLTQxNTgtOGZmMS02MTY0YzM5OWNlNDMiLCJpYXQiOjE1Mjg4MjE1ODUuNDM0LCJleHAiOjE1Mjg4MjE3NjUuNDM0LCJpc3MiOiJodHRwczovL2FwaS5jaXZpYy5jb20vand0IiwiYXVkIjoiQXR0ZXN0ZXJTZXJ2aWNlIiwic3ViIjoiYzhhNjRhODE4NWRlMzNkMTlkZTgwMjFmYmUyMjhkMmE1YTc3YzExMTdkYjc1NDJlZDE0ODM1NGNiZjdkNGVmMSIsImRhdGEiOnsibWV0aG9kIjoiUE9TVCIsInBhdGgiOiJodHRwczovL2Rldi5hcGkuY2l2aWMuY29tL3JlcXVlc3QtYXR0ZXN0YXRpb24tdGJjaC9yZXF1ZXN0QXR0ZXN0YXRpb24ifX0.2Rp8XLTLvzu51raTQRpce8kIiilsMeiPZeWAsuNv5n7hFZGl-ce-fx9DgxsZ0OTaIUgo8frbiGmHjQh0WlUG7A' } + response: { + jwt: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJqdGkiOiIyYzdlNjQ4YS1hNDhmLTQxNTgtOGZmMS02MTY0YzM5OWNlNDMiLCJpYXQiOjE1Mjg4MjE1ODUuNDM0LCJleHAiOjE1Mjg4MjE3NjUuNDM0LCJpc3MiOiJodHRwczovL2FwaS5jaXZpYy5jb20vand0IiwiYXVkIjoiQXR0ZXN0ZXJTZXJ2aWNlIiwic3ViIjoiYzhhNjRhODE4NWRlMzNkMTlkZTgwMjFmYmUyMjhkMmE1YTc3YzExMTdkYjc1NDJlZDE0ODM1NGNiZjdkNGVmMSIsImRhdGEiOnsibWV0aG9kIjoiUE9TVCIsInBhdGgiOiJodHRwczovL2Rldi5hcGkuY2l2aWMuY29tL3JlcXVlc3QtYXR0ZXN0YXRpb24tdGJjaC9yZXF1ZXN0QXR0ZXN0YXRpb24ifX0.2Rp8XLTLvzu51raTQRpce8kIiilsMeiPZeWAsuNv5n7hFZGl-ce-fx9DgxsZ0OTaIUgo8frbiGmHjQh0WlUG7A' + } }, { path: '/requestAttestation', - response: { statusUrl: '/status/372091f0-6e5f-11e8-ab04-8d6f9c9b4e5a' } + response: { + statusUrl: '/status/372091f0-6e5f-11e8-ab04-8d6f9c9b4e5a' + } }, { path: '/requestAttestation', - response: { statusUrl: '/status/372091f0-6e5f-11e8-ab04-8d6f9c9b4e5a' } + response: { + statusUrl: '/status/372091f0-6e5f-11e8-ab04-8d6f9c9b4e5a' + } }, { path: '/status', response: { @@ -54,27 +70,28 @@ function HttpServiceConstructor() { type: 'temporary', network: 'testnet' } - }]; - const res = _.find(responses, function (r) { - return _.includes(params.url, r.path); - }); + + const res = _.find(responses, r => _.includes(params.url, r.path)); + if (res) { return Promise.resolve(res.response); } + return Promise.reject(); }); return function (_x, _x2) { return _ref.apply(this, arguments); }; - })(); + }(); + return this; } logger.debug('Using Mock HTTP Service'); const http = new HttpServiceConstructor(); http.request('/status').then(console.log); // eslint-disable-line -logger.debug(`HTTP Service instance ${JSON.stringify(http, null, 2)}`); +logger.debug(`HTTP Service instance ${JSON.stringify(http, null, 2)}`); module.exports = http; \ No newline at end of file diff --git a/dist/cjs/services/anchorService.js b/dist/cjs/services/anchorService.js index cba30fc7..ce5daf62 100644 --- a/dist/cjs/services/anchorService.js +++ b/dist/cjs/services/anchorService.js @@ -7,13 +7,21 @@ */ function Anchor(impl) { this.impl = impl; + this.anchor = (label, data, options) => this.impl.anchor(label, data, options); + this.update = tempAnchor => this.impl.update(tempAnchor); + this.verifySignature = subject => this.impl.verifySignature(subject); + this.verifySubjectSignature = subject => this.impl.verifySubjectSignature(subject); + this.verifyAttestation = signature => this.impl.verifyAttestation(signature); + this.revokeAttestation = signature => this.impl.revokeAttestation(signature); + this.isRevoked = signature => this.impl.isRevoked(signature); + return this; } diff --git a/dist/cjs/services/config.js b/dist/cjs/services/config.js index 06c07403..cc145f72 100644 --- a/dist/cjs/services/config.js +++ b/dist/cjs/services/config.js @@ -1,33 +1,32 @@ -'use strict'; +"use strict"; const path = require('path'); + const os = require('os'); + const fs = require('fs'); const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined'; - if (process.platform === 'win32') throw new Error(`Unsupported platform: ${process.platform}`); if (process.env.APP_ENV !== 'browser' && !isBrowser) { const CONFIG_FILE = 'config'; - const CONFIG_PATH = { BOX: '/etc/civic', USER: path.join(`${os.homedir()}`, '.civic') }; - const userConfigFile = path.join(CONFIG_PATH.USER, CONFIG_FILE); const boxConfigFile = path.join(CONFIG_PATH.BOX, CONFIG_FILE); - const configFile = fs.existsSync(userConfigFile) ? userConfigFile : boxConfigFile; - /* eslint-disable global-require */ + if (fs.existsSync(userConfigFile)) { require('dotenv').config({ path: configFile }); } /* eslint-ebable global-require */ + } const config = { @@ -41,10 +40,11 @@ const config = { } }, passphrase: process.env.CIVIC_PASSPHRASE, - keychain: { prv: process.env.CIVIC_KEYCHAIN }, + keychain: { + prv: process.env.CIVIC_KEYCHAIN + }, accessToken: process.env.CLIENT_ACCESS_TOKEN, walletId: process.env.CLIENT_WALLET_ID, walletPassphrase: process.env.CLIENT_WALLET_PASSPHRASE }; - module.exports = config; \ No newline at end of file diff --git a/dist/cjs/services/httpService.js b/dist/cjs/services/httpService.js index 12580b16..1653dc2e 100644 --- a/dist/cjs/services/httpService.js +++ b/dist/cjs/services/httpService.js @@ -1,16 +1,18 @@ -'use strict'; +"use strict"; -function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } +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); }); }; } /** * A simple node HTTP services */ -const request = require('request-promise-native'); -// uncomment to debug requests +const request = require('request-promise-native'); // uncomment to debug requests // require('request-debug')(request); + function HttpServiceConstructor() { - this.request = (() => { + this.request = /*#__PURE__*/function () { var _ref = _asyncToGenerator(function* (uri, options) { const response = yield request(uri, options); return response; @@ -19,7 +21,8 @@ function HttpServiceConstructor() { return function (_x, _x2) { return _ref.apply(this, arguments); }; - })(); + }(); + return this; } diff --git a/dist/cjs/services/index.js b/dist/cjs/services/index.js index 9153b3a3..078aad14 100644 --- a/dist/cjs/services/index.js +++ b/dist/cjs/services/index.js @@ -1,58 +1,68 @@ -'use strict'; +"use strict"; /** * Services IoC modules */ const Bottle = require('bottlejs'); -const { CurrentCivicAnchor } = require('./DefaultAnchorServiceImpl.js'); + +const { + CurrentCivicAnchor +} = require('./DefaultAnchorServiceImpl'); + const logger = require('../logger'); + const HttpServiceConstructor = require('./httpService'); + const config = require('./config'); + const SecureRandom = require('../SecureRandom'); + const MiniCryptoManagerImpl = require('./MiniCryptoManagerImpl'); const services = new Bottle(); - /** * Init services with new values to config and http services * @param {*} conf * @param {*} http * @param secureRandom */ + const initServices = (conf, http, secureRandom, cryptoManagerImpl) => { if (http) { services.resetProviders(['Http']); logger.debug('Registering custom HTTP service implementation'); services.factory('Http', () => http); } + if (conf) { services.resetProviders(['Config']); logger.debug('Registering custom Config service implementation'); services.factory('Config', () => conf); } + if (secureRandom) { services.resetProviders(['SecureRandom']); logger.debug('Registering custom SecureRandom service implementation'); services.factory('SecureRandom', () => secureRandom); } + if (cryptoManagerImpl) { services.resetProviders(['CryptoManager']); logger.debug('Registering custom CryptoManager service implementation'); services.factory('CryptoManager', () => cryptoManagerImpl); } + return services; }; services.factory('Config', () => config); - logger.info('Registering request-promise-native as Http service implementation.'); services.service('Http', HttpServiceConstructor); - services.factory('SecureRandom', () => new SecureRandom()); +services.service('AnchorService', CurrentCivicAnchor, 'Config', 'Http'); // The default CryptoManager Implementation -services.service('AnchorService', CurrentCivicAnchor, 'Config', 'Http'); - -// The default CryptoManager Implementation services.service('CryptoManager', MiniCryptoManagerImpl); - -module.exports = { services, initServices }; \ No newline at end of file +module.exports = { + services, + initServices +}; \ No newline at end of file diff --git a/dist/cjs/timeHelper.js b/dist/cjs/timeHelper.js index 85646d00..480fc372 100644 --- a/dist/cjs/timeHelper.js +++ b/dist/cjs/timeHelper.js @@ -1,4 +1,4 @@ -'use strict'; +"use strict"; const moment = require('moment-mini'); @@ -8,28 +8,26 @@ const unitMapper = { w: 'w', d: 'd' }; - /** * Convert a delta string like "21y" to a moment Duration object * @param delta * @return {moment.Duration} */ + const timeDeltaToMomentDuration = delta => { const matched = delta.match(/(-?\d+)(\w)/); - if (!matched) throw new Error(`Invalid time delta ${delta}`); - const [, amount, unit] = matched; - return moment.duration(parseInt(amount, 10), unitMapper[unit]); }; - /** * Given a time delta like "-21y", apply it to the passed in date object, or the current time * @param delta String * @param date Date * @return {Date} */ + + const applyDeltaToDate = (delta, date = new Date()) => moment(date).add(timeDeltaToMomentDuration(delta)).toDate(); module.exports = { diff --git a/dist/cjs/uca/UCA.js b/dist/cjs/uca/UCA.js new file mode 100644 index 00000000..26c70a4d --- /dev/null +++ b/dist/cjs/uca/UCA.js @@ -0,0 +1,27 @@ +"use strict"; + +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); }); }; } + +const { + UserCollectableAttribute: BaseUCA +} = require('@identity.com/uca'); + +const { + schemaLoader +} = require('../schemas/jsonSchema'); + +class UserCollectableAttribute extends BaseUCA { + static create(identifier, value, version) { + return _asyncToGenerator(function* () { + yield schemaLoader.loadSchemaFromTitle(identifier); + return new UserCollectableAttribute(identifier, value, version, schemaLoader.ucaDefinitions); + })(); + } + +} + +module.exports = { + UserCollectableAttribute +}; \ No newline at end of file diff --git a/dist/es/AggregationHandler.js b/dist/es/AggregationHandler.js index 10f53b69..d14084f3 100644 --- a/dist/es/AggregationHandler.js +++ b/dist/es/AggregationHandler.js @@ -4,37 +4,49 @@ const validateEmptyParametersOperators = parameters => { if (!_.isEmpty(parameters)) { throw new Error('parameters should be empty'); } + return true; }; + const validateNotEmptyParametersOperators = parameters => { if (!parameters && _.isEmpty(parameters)) { throw new Error('parameters should not be empty'); } + return true; }; + const validatePathParametersOperators = parameters => { if (!_.isString(parameters)) { throw new Error('parameters should be string'); } + return true; }; + const validateNumberParametersOperators = parameters => { if (!_.isNumber(parameters)) { throw new Error('parameters should be number'); } + return true; }; + const validateObjectParametersOperators = parameters => { if (!_.isObject(parameters)) { throw new Error('parameters should be object'); } + return true; }; const sort = (colllection, params) => { const path = _.keys(params)[0]; + const order = params[path]; + const ordered = _.sortBy(colllection, path); + return order === 'ASC' ? ordered : _.reverse(ordered); }; @@ -50,16 +62,19 @@ const AGGREGATION_OPERATORS_MAP = { function aggregate(credentials, stages) { let filtered = [...credentials]; + _.forEach(stages, stage => { const operator = _.keys(stage)[0]; if (!_.includes(_.keys(AGGREGATION_OPERATORS_MAP), operator)) { throw new Error(`Invalid operator: ${operator}`); } + const params = stage[operator]; const operatorImplementation = AGGREGATION_OPERATORS_MAP[operator]; filtered = operatorImplementation(filtered, params); }); + return filtered; } diff --git a/dist/es/SecureRandom.js b/dist/es/SecureRandom.js index 7d560a04..ac85a4d5 100644 --- a/dist/es/SecureRandom.js +++ b/dist/es/SecureRandom.js @@ -1,10 +1,11 @@ const sjcl = require('sjcl'); + const logger = require('./logger'); class SecureRandom { constructor(seedHexString) { - logger.debug('Init Secure Random'); - // eslint-disable-next-line new-cap + logger.debug('Init Secure Random'); // eslint-disable-next-line new-cap + this.sjclRandom = new sjcl.prng(10); if (seedHexString) { @@ -15,8 +16,11 @@ class SecureRandom { try { logger.debug('Trying crypto'); /* eslint-disable global-require */ + const hexString = require('crypto').randomBytes(1024).toString('hex'); /* eslint-enable global-require */ + + const seed = sjcl.codec.hex.toBits(hexString); this.sjclRandom.addEntropy(seed, undefined, 'csprng'); this.isSeeded = true; @@ -35,6 +39,7 @@ class SecureRandom { const randomBytes = this.sjclRandom.randomWords(size / 8, 10); return sjcl.codec.hex.fromBits(randomBytes); } + } module.exports = SecureRandom; \ No newline at end of file diff --git a/dist/es/claim/Claim.js b/dist/es/claim/Claim.js index c62a2e26..c4eb8f33 100644 --- a/dist/es/claim/Claim.js +++ b/dist/es/claim/Claim.js @@ -1,53 +1,103 @@ const _ = require('lodash'); + const sjcl = require('sjcl'); -const { UserCollectableAttribute } = require('@identity.com/uca'); + +const { + UserCollectableAttribute +} = require('../uca/UCA'); + +const { + services +} = require('../services'); + const definitions = require('./definitions'); -const { services } = require('../services'); -const validIdentifiers = _.map(definitions, d => d.identifier); +const { + schemaLoader +} = require('../schemas/jsonSchema'); + +const { + validIdentifiers +} = schemaLoader; + +const findDefinition = (identifier, version) => version ? _.find(definitions, { + identifier, + version +}) : _.find(definitions, { + identifier +}); -const getDefinition = (identifier, version) => version ? _.find(definitions, { identifier, version }) : _.find(definitions, { identifier }); +const getDefinition = async (identifier, version) => { + await schemaLoader.loadSchemaFromTitle(identifier); + return findDefinition(identifier, version); +}; const isArrayAttestableValue = aValue => aValue.indexOf('[') > -1 && aValue.indexOf(']') > -1; function getBaseIdentifiers(identifier) { - const claimRegex = /claim-cvc:(.*)\.(.*)-v\d*/; + const claimRegex = /^claim-cvc:(.*)\.(.*)-v\d*$/; let isNewIdentifier = true; - let identifierComponents = claimRegex.exec(identifier); + if (identifierComponents === null) { identifierComponents = _.split(identifier, ':'); isNewIdentifier = false; } - return { identifierComponents, isNewIdentifier }; + + return { + identifierComponents, + isNewIdentifier + }; } -function adaptIdentifierIfNeeded(identifier, version) { - const definition = getDefinition(identifier, version); +async function adaptIdentifierIfNeeded(identifier, version) { + const definition = await getDefinition(identifier, version); const resolvedIdentifier = definition && definition.alias ? definition.type : identifier; + const { + isNewIdentifier, + identifierComponents + } = getBaseIdentifiers(resolvedIdentifier); + const compDefinition = await getDefinition(resolvedIdentifier, version); - const { isNewIdentifier, identifierComponents } = getBaseIdentifiers(resolvedIdentifier); - - if (!isNewIdentifier && !getDefinition(resolvedIdentifier, version)) { + if (!isNewIdentifier && !compDefinition) { const newIdentifier = `claim-cvc:${identifierComponents[1]}.${identifierComponents[2]}-v1`; - const foundNewIdentifier = _.find(definitions, { identifier: newIdentifier }); + await schemaLoader.loadSchemaFromTitle(newIdentifier); + + const foundNewIdentifier = _.find(definitions, { + identifier: newIdentifier + }); + if (foundNewIdentifier) { return newIdentifier; } + throw new Error(`${resolvedIdentifier} is not defined`); } + return identifier; } class Claim extends UserCollectableAttribute { constructor(identifier, value, version) { - const currentIdentifier = adaptIdentifierIfNeeded(identifier, version); - super(currentIdentifier, value, version, definitions); - this.initialize(currentIdentifier, value, version); + super(identifier, value, version, definitions); + this.initialize(identifier, value, version); + } + + static async create(identifier, value, version) { + const currentIdentifier = await adaptIdentifierIfNeeded(identifier, version); + + if (!value.attestableValue) { + await schemaLoader.validateSchema(currentIdentifier, value); + } // Load the schema and it's references from a source to be used for validation and defining the schema definitions + + + await schemaLoader.loadSchemaFromTitle(currentIdentifier); + return new Claim(currentIdentifier, value, version); } initialize(identifier, value, version) { super.initialize(identifier, value, version); + if (!this.salt) { const secureRandom = services.container.SecureRandom; this.salt = sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash(secureRandom.wordWith(64))); @@ -55,9 +105,8 @@ class Claim extends UserCollectableAttribute { } initializeValuesWithArrayItems(identifier, values, version) { - const definition = getDefinition(this.identifier, this.version); + const definition = findDefinition(this.identifier, this.version); const ucaArray = []; - if (!_.isArray(values)) throw new Error(`Value for ${identifier}-${version} should be an array`); _.forEach(values, value => { @@ -69,52 +118,64 @@ class Claim extends UserCollectableAttribute { } initializeAttestableValue() { - const { value } = this; - const definition = getDefinition(this.identifier, this.version); - const parsedAttestableValue = Claim.parseAttestableValue(value); + const { + value + } = this; + const definition = findDefinition(this.identifier, this.version); + const parsedAttestableValue = Claim.parseAttestableValue(value); // Trying to construct UCA with a existing attestableValue - // Trying to construct UCA with a existing attestableValue if (parsedAttestableValue.length === 1) { // This is a simple attestableValue this.timestamp = null; this.salt = parsedAttestableValue[0].salt; const ucaValue = parsedAttestableValue[0].value; - this.value = definition.type === 'Array' ? _.map(ucaValue, item => new Claim(definition.items.type, { attestableValue: item })) : this.value = _.includes(['null', 'undefined'], ucaValue) ? null : ucaValue; + this.value = definition.type === 'Array' ? _.map(ucaValue, item => new Claim(definition.items.type, { + attestableValue: item + })) : this.value = _.includes(['null', 'undefined'], ucaValue) ? null : ucaValue; } else { const ucaValue = {}; + for (let i = 0; i < parsedAttestableValue.length; i += 1) { - const { propertyName } = parsedAttestableValue[i]; - // we have stored only the property name on the urn, so we have to find the UCA definition + const { + propertyName + } = parsedAttestableValue[i]; // we have stored only the property name on the urn, so we have to find the UCA definition let filteredIdentifier; let ucaPropertyName; const ucaType = UserCollectableAttribute.resolveType(definition, definitions); const ucaDef = ucaType.properties.find(prop => prop.name === propertyName); + if (ucaDef) { filteredIdentifier = ucaDef.type; ucaPropertyName = propertyName; } else { - const splitPropertyName = propertyName.split('.'); - // this property is used to check if the recursion tree has more than an depth + const splitPropertyName = propertyName.split('.'); // this property is used to check if the recursion tree has more than an depth + const ucaNamespace = splitPropertyName[splitPropertyName.length - 2]; const ucaNamespacePascal = ucaNamespace.substring(0, 1).toUpperCase() + ucaNamespace.substring(1); ucaPropertyName = splitPropertyName[splitPropertyName.length - 1]; filteredIdentifier = `cvc:${ucaNamespacePascal}:${ucaPropertyName}`; - } + } // test if definition exists + - // test if definition exists const filteredDefinition = definitions.find(def => def.identifier === filteredIdentifier); + if (!filteredDefinition) { // this must have an claim path with no recursive definition filteredIdentifier = this.findDefinitionByAttestableValue(ucaPropertyName, definition); } - ucaValue[propertyName] = new Claim(filteredIdentifier, { attestableValue: parsedAttestableValue[i].stringValue }); + + ucaValue[propertyName] = new Claim(filteredIdentifier, { + attestableValue: parsedAttestableValue[i].stringValue + }); } + this.value = ucaValue; } } - /* eslint-disable class-methods-use-this */ + + getValidIdentifiers() { return validIdentifiers; } @@ -125,13 +186,18 @@ class Claim extends UserCollectableAttribute { static parseAttestableArrayValue(value) { const splitDots = value.attestableValue.split(':'); - const propertyName = splitDots[1]; const salt = splitDots[2]; const attestableValueItems = value.attestableValue.substring(value.attestableValue.indexOf('[') + 1, value.attestableValue.indexOf(']') - 1).split(','); - const parsedArrayItems = _.map(attestableValueItems, item => Claim.parseAttestableValue({ attestableValue: item })); + + const parsedArrayItems = _.map(attestableValueItems, item => Claim.parseAttestableValue({ + attestableValue: item + })); + return { - propertyName, salt, value: parsedArrayItems + propertyName, + salt, + value: parsedArrayItems }; } @@ -146,13 +212,16 @@ class Claim extends UserCollectableAttribute { if (isArrayAttestableValue(value.attestableValue)) { const arrayValues = Claim.parseAttestableArrayValue(value); return [arrayValues]; - } + } // If is not an ArrayValue we parse it now + - // If is not an ArrayValue we parse it now const splitPipes = _.split(value.attestableValue, '|'); + const attestableValueRegex = /^urn:(\w+(?:\.\w+)*):(\w+):(.+)/; + _.each(splitPipes, stringValue => { const match = attestableValueRegex.exec(stringValue); + if (match && match.length === 4) { const v = { propertyName: match[1], @@ -163,31 +232,42 @@ class Claim extends UserCollectableAttribute { values.push(v); } }); + if (splitPipes.length !== values.length && splitPipes.length !== values.length + 1) { throw new Error('Invalid attestableValue'); } + return values; } findDefinitionByAttestableValue(attestableValuePropertyName, rootDefinition) { const ucaType = UserCollectableAttribute.resolveType(rootDefinition, definitions); + for (const property of ucaType.properties) { // eslint-disable-line no-restricted-syntax - const resolvedDefinition = _.find(definitions, { identifier: property.type }); + const resolvedDefinition = _.find(definitions, { + identifier: property.type + }); + resolvedDefinition.type = UserCollectableAttribute.resolveType(resolvedDefinition, definitions); + if (!resolvedDefinition.type.properties && property.name === attestableValuePropertyName) { return property.type; } + if (resolvedDefinition.type.properties) { return this.findDefinitionByAttestableValue(attestableValuePropertyName, resolvedDefinition); } } + return null; } getAttestableValue(path, isArrayItem = false) { // all UCA properties they have the form of :propertyName or :something.propertyName - const { identifierComponents } = getBaseIdentifiers(this.identifier); + const { + identifierComponents + } = getBaseIdentifiers(this.identifier); let propertyName = identifierComponents[2]; if (isArrayItem) { @@ -197,32 +277,41 @@ class Claim extends UserCollectableAttribute { if (path) { propertyName = `${path}.${propertyName}`; - } + } // it was defined that the attestable value would be on the URN type https://tools.ietf.org/html/rfc8141 + - // it was defined that the attestable value would be on the URN type https://tools.ietf.org/html/rfc8141 if (['String', 'Number', 'Boolean'].indexOf(this.type) >= 0) { return `urn:${propertyName}:${this.salt}:${this.value}|`; - }if (this.type === 'Array') { + } + + if (this.type === 'Array') { const itemsValues = _.reduce(this.value, (result, item) => `${result}${item.getAttestableValue(null, true)},`, ''); + return `urn:${propertyName}:${this.salt}:[${itemsValues}]`; } + return _.reduce(_.sortBy(_.keys(this.value)), (s, k) => `${s}${this.value[k].getAttestableValue(propertyName)}`, ''); } - /** - * Returns the global CredentialItem of the Credential - */ + * Returns the global CredentialItem of the Credential + */ + + getGlobalIdentifier() { return `claim-${this.identifier}-${this.version}`; } getClaimRootPropertyName() { - const { identifierComponents } = getBaseIdentifiers(this.identifier); + const { + identifierComponents + } = getBaseIdentifiers(this.identifier); return identifierComponents[1].toLowerCase() === 'type' ? '' : _.camelCase(identifierComponents[1]); } getClaimPropertyName() { - const { identifierComponents } = getBaseIdentifiers(this.identifier); + const { + identifierComponents + } = getBaseIdentifiers(this.identifier); return identifierComponents[2]; } @@ -231,8 +320,12 @@ class Claim extends UserCollectableAttribute { } static getPath(identifier) { - const { identifierComponents } = getBaseIdentifiers(identifier); + const { + identifierComponents + } = getBaseIdentifiers(identifier); + const baseName = _.camelCase(identifierComponents[1]); + return baseName !== 'type' ? `${baseName}.${identifierComponents[2]}` : identifierComponents[2]; } @@ -241,15 +334,27 @@ class Claim extends UserCollectableAttribute { const headComponents = head ? _.split(head, '.') : []; let tailComponents = tail ? _.split(tail, '.') : []; tailComponents = _.last(headComponents) === _.first(tailComponents) ? tailComponents.splice(1) : tailComponents; + const newPath = _.join([...headComponents, ...tailComponents], '.'); + return newPath; }; let values = []; - const def = _.find(definitions, { identifier: this.identifier, version: this.version }); + + const def = _.find(definitions, { + identifier: this.identifier, + version: this.version + }); + if (def.credentialItem || def.attestable) { const claimPath = joinPaths(path, !isItemArray ? this.getClaimPath() : null); - values.push({ identifier: this.identifier, value: this.getAttestableValue(null, isItemArray), claimPath }); + values.push({ + identifier: this.identifier, + value: this.getAttestableValue(null, isItemArray), + claimPath + }); + if (this.type === 'Object') { _.forEach(_.keys(this.value), k => { const innerValues = this.value[k].getAttestableValues(claimPath); @@ -261,42 +366,62 @@ class Claim extends UserCollectableAttribute { }); } } + return values; } - /** - * extract the expected Type name for the value when constructing an UCA - * @param {*} definition - */ + * extract the expected Type name for the value when constructing an UCA + * @param {*} definition + */ + + static getTypeName(definition) { if (_.isString(definition.type)) { if (_.includes(validIdentifiers, definition.type)) { - const innerDefinition = _.find(definitions, { identifier: definition.type }); + const innerDefinition = _.find(definitions, { + identifier: definition.type + }); + return this.getTypeName(innerDefinition); } return definition.type; } + return 'Object'; } - static getAllProperties(identifier, pathName) { - const definition = _.find(definitions, { identifier }); + static async getAllProperties(identifier, pathName) { + await schemaLoader.loadSchemaFromTitle(identifier); + + const definition = _.find(definitions, { + identifier + }); + const properties = []; const type = UserCollectableAttribute.resolveType(definition, definitions); - const typeDefinition = _.isString(type) ? _.find(definitions, { identifier: type }) : definition; + const typeDefinition = _.isString(type) ? _.find(definitions, { + identifier: type + }) : definition; if (typeDefinition && this.getTypeName(typeDefinition) === 'Object') { let typeDefProps; + if (typeDefinition.type.properties) { typeDefProps = typeDefinition.type.properties; } else { - const typeDefDefinition = _.find(definitions, { identifier: typeDefinition.type }); + const typeDefDefinition = _.find(definitions, { + identifier: typeDefinition.type + }); + typeDefProps = UserCollectableAttribute.resolveType(typeDefDefinition, definitions).properties; } let basePropName; - const { identifierComponents: baseIdentifierComponents } = getBaseIdentifiers(identifier); + const { + identifierComponents: baseIdentifierComponents + } = getBaseIdentifiers(identifier); + if (pathName) { if (_.includes(pathName, _.lowerCase(baseIdentifierComponents[1]))) { basePropName = `${pathName}`; @@ -311,35 +436,51 @@ class Claim extends UserCollectableAttribute { // Properties is not an object properties.push(`${basePropName}.${typeDefProps.name}`); } else { - _.forEach(typeDefProps, prop => { - const { isNewIdentifier } = getBaseIdentifiers(prop.type); + const proProperties = await typeDefProps.reduce(async (prev, prop) => { + const prevProps = await prev; + const { + isNewIdentifier + } = getBaseIdentifiers(prop.type); const newBasePropName = !isNewIdentifier ? basePropName : `${basePropName}.${prop.name}`; - const proProperties = this.getAllProperties(prop.type, newBasePropName); - _.forEach(proProperties, p => properties.push(p)); - }); + const props = await this.getAllProperties(prop.type, newBasePropName); + return [...prevProps, ...props]; + }, Promise.resolve([])); + properties.push(...proProperties); } } else if (pathName) { - const { identifierComponents } = getBaseIdentifiers(definition.identifier); + const { + identifierComponents + } = getBaseIdentifiers(definition.identifier); let propertiesName; + if (pathName.indexOf(identifierComponents[2]) >= 0) { propertiesName = `${pathName}`; } else { propertiesName = `${pathName}.${identifierComponents[2]}`; } + properties.push(propertiesName); } else { - const { identifierComponents } = getBaseIdentifiers(identifier); + const { + identifierComponents + } = getBaseIdentifiers(identifier); const propertiesName = `${_.lowerCase(identifierComponents[1])}.${identifierComponents[2]}`; properties.push(propertiesName); } + return properties; } + } function convertIdentifierToClassName(identifier) { - const { identifierComponents } = getBaseIdentifiers(identifier); + const { + identifierComponents + } = getBaseIdentifiers(identifier); const baseName = identifierComponents[1]; + const detailName = _.upperFirst(_.camelCase(identifierComponents[2])); + return `${baseName}${detailName}`; } @@ -348,16 +489,24 @@ function mixinIdentifiers(UCA) { _.forEach(_.filter(definitions, d => d.credentialItem), def => { const name = convertIdentifierToClassName(def.identifier); const source = {}; - const { identifier } = def; + const { + identifier + } = def; function UCAConstructor(value, version) { return new Claim(identifier, value, version); } source[name] = UCAConstructor; + _.mixin(Claim, source); }); + return UCA; } -module.exports = { Claim: mixinIdentifiers(Claim), definitions, getBaseIdentifiers }; \ No newline at end of file +module.exports = { + Claim: mixinIdentifiers(Claim), + definitions, + getBaseIdentifiers +}; \ No newline at end of file diff --git a/dist/es/claim/__mocks__/definitions.js b/dist/es/claim/__mocks__/definitions.js index bd06f160..b910c05a 100644 --- a/dist/es/claim/__mocks__/definitions.js +++ b/dist/es/claim/__mocks__/definitions.js @@ -27,5 +27,4 @@ const definitions = [{ exclusiveMinimum: false, minimum: 5 }]; - module.exports = definitions; \ No newline at end of file diff --git a/dist/es/claim/definitions.js b/dist/es/claim/definitions.js index 94b50211..81ab88b8 100644 --- a/dist/es/claim/definitions.js +++ b/dist/es/claim/definitions.js @@ -1,616 +1,2 @@ -const { definitions: ucaDefinitions } = require('@identity.com/uca'); - -// ######################################### DEFINITIONS ########################################### -const definitions = [{ - identifier: 'claim-cvc:Email.domain-v1', - version: '1', - type: 'cvc:Type:domain', - credentialItem: true -}, { - identifier: 'claim-cvc:Contact.email-v1', - version: '1', - type: 'claim-cvc:Type.email-v1', - credentialItem: true -}, { - identifier: 'claim-cvc:User.id-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:User.realm-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Phone.countryCode-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Phone.number-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Phone.extension-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Phone.lineType-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:PhoneNumber.countryCode-v1', - type: 'claim-cvc:Phone.countryCode-v1', - version: '1', - credentialItem: true -}, { - identifier: 'claim-cvc:PhoneNumber.number-v1', - type: 'claim-cvc:Phone.number-v1', - version: '1', - credentialItem: true -}, { - identifier: 'claim-cvc:PhoneNumber.extension-v1', - type: 'claim-cvc:Phone.extension-v1', - version: '1', - credentialItem: true -}, { - identifier: 'claim-cvc:PhoneNumber.lineType-v1', - type: 'claim-cvc:Phone.lineType-v1', - version: '1', - credentialItem: true -}, { - identifier: 'claim-cvc:Contact.phoneNumber-v1', - version: '1', - type: 'claim-cvc:Type.phoneNumber-v1', - credentialItem: true -}, { - identifier: 'claim-cvc:Name.givenNames-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Name.familyNames-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Name.otherNames-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Type.Name-v1', - version: '1', - type: { - properties: [{ - name: 'givenNames', - type: 'claim-cvc:Name.givenNames-v1' - }, { - name: 'familyNames', - type: 'claim-cvc:Name.familyNames-v1' - }, { - name: 'otherNames', - type: 'claim-cvc:Name.otherNames-v1' - }], - required: ['givenNames'] - }, - credentialItem: true -}, { - identifier: 'claim-cvc:Document.name-v1', - version: '1', - type: 'claim-cvc:Type.Name-v1', - credentialItem: true -}, { - identifier: 'claim-cvc:Identity.name-v1', - version: '1', - type: 'claim-cvc:Type.Name-v1', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.nationality-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.number-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Identity.dateOfBirth-v1', - version: '1', - type: 'cvc:Type:date', - credentialItem: true -}, { - identifier: 'claim-cvc:Address.city-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Address.postalCode-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Address.state-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Address.county-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Address.country-v1', - version: '1', - type: 'cvc:Type:country', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.address-v1', - version: '1', - type: 'claim-cvc:Type.address-v1', - credentialItem: true -}, { - identifier: 'claim-cvc:Identity.address-v1', - version: '1', - type: 'claim-cvc:Type.address-v1', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.dateOfIssue-v1', - version: '1', - type: 'cvc:Type:date', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.dateOfExpiry-v1', - version: '1', - type: 'cvc:Type:date', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.dateOfBirth-v1', - version: '1', - type: 'cvc:Type:date', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.enum-v1', - version: '1', - type: 'String' -}, { - identifier: 'claim-cvc:Document.type-v1', - version: '1', - type: 'cvc:Type:documentType', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.gender-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.issueLocation-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.issueAuthority-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.issueCountry-v1', - version: '1', - type: 'cvc:Type:country', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.placeOfBirth-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Type.email-v1', - version: '1', - type: { - properties: [{ - name: 'username', - type: 'cvc:Email:username' - }, { - name: 'domain', - type: 'claim-cvc:Email.domain-v1' - }] - }, - credentialItem: true -}, { - identifier: 'claim-cvc:Type.phoneNumber-v1', - version: '1', - type: { - properties: [{ - name: 'country', - type: 'claim-cvc:PhoneNumber.country-v1' - }, { - name: 'countryCode', - type: 'claim-cvc:PhoneNumber.countryCode-v1' - }, { - name: 'number', - type: 'claim-cvc:PhoneNumber.number-v1' - }, { - name: 'extension', - type: 'claim-cvc:PhoneNumber.extension-v1' - }, { - name: 'lineType', - type: 'claim-cvc:PhoneNumber.lineType-v1' - }], - required: ['country', 'countryCode', 'number', 'lineType'] - }, - credentialItem: true -}, { - identifier: 'claim-cvc:PhoneNumber.country-v1', - type: 'cvc:Type:country', - version: '1', - credentialItem: true -}, { - identifier: 'claim-cvc:Type.Name-v1', - version: '1', - type: { - properties: [{ - name: 'givenNames', - type: 'claim-cvc:Name.givenNames-v1' - }, { - name: 'familyNames', - type: 'claim-cvc:Name.familyNames-v1' - }, { - name: 'otherNames', - type: 'claim-cvc:Name.otherNames-v1' - }], - required: ['givenNames'] - }, - credentialItem: true -}, { - identifier: 'claim-cvc:Type.address-v1', - version: '1', - type: { - properties: [{ - name: 'country', - type: 'claim-cvc:Address.country-v1' - }, { - name: 'county', - type: 'claim-cvc:Address.county-v1' - }, { - name: 'state', - type: 'claim-cvc:Address.state-v1' - }, { - name: 'street', - type: 'claim-cvc:Address.street-v1' - }, { - name: 'unit', - type: 'claim-cvc:Address.unit-v1' - }, { - name: 'city', - type: 'claim-cvc:Address.city-v1' - }, { - name: 'postalCode', - type: 'claim-cvc:Address.postalCode-v1' - }], - required: ['street', 'city', 'state', 'country'] - }, - credentialItem: true -}, { - identifier: 'claim-cvc:Address.street-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Address.unit-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.properties-v1', - version: '1', - attestable: true, - type: { - properties: [{ - name: 'dateOfIssue', - type: 'claim-cvc:Document.dateOfIssue-v1' - }, { - name: 'dateOfExpiry', - type: 'claim-cvc:Document.dateOfExpiry-v1' - }], - required: ['dateOfIssue'] - }, - credentialItem: true -}, { - identifier: 'claim-cvc:SocialSecurity.number-v1', - version: '1', - type: 'cvc:Type:socialSecurityNumber', - credentialItem: true -}, { - identifier: 'claim-cvc:Validation:evidences.idDocumentFront-v1', - version: '1', - type: 'cvc:Evidences:idDocumentFront', - credentialItem: true -}, { - identifier: 'claim-cvc:Validation:evidences.idDocumentBack-v1', - version: '1', - type: 'cvc:Evidences:idDocumentBack', - credentialItem: true -}, { - identifier: 'claim-cvc:Validation:evidences.selfie-v1', - version: '1', - type: 'cvc:Evidences:selfie', - credentialItem: true -}, { - identifier: 'claim-cvc:Document.evidences-v1', - version: '1', - attestable: true, - type: { - properties: [{ - name: 'idDocumentFront', - type: 'claim-cvc:Validation:evidences.idDocumentFront-v1' - }, { - name: 'idDocumentBack', - type: 'claim-cvc:Validation:evidences.idDocumentBack-v1' - }, { - name: 'selfie', - type: 'claim-cvc:Validation:evidences.selfie-v1' - }] - }, - credentialItem: true -}, { - identifier: 'claim-cvc:Vaccination.date-v1', - version: '1', - type: 'cvc:Type:timestamp', - credentialItem: false -}, { - identifier: 'claim-cvc:Test.date-v1', - version: '1', - type: 'cvc:Type:timestamp', - credentialItem: false -}, { - identifier: 'claim-cvc:Vaccination.name-v1', - version: '1', - type: 'String', - credentialItem: false -}, { - identifier: 'claim-cvc:Vaccination.recordDetail-v1', - version: '1', - type: { - properties: [{ - name: 'createdAt', - type: 'cvc:Type:date' - }, { - name: 'updatedAt', - type: 'cvc:Type:date' - }] - }, - required: ['createdAt'], - credentialItem: false -}, { - identifier: 'claim-cvc:Type.organizationName-v1', - version: '1', - type: 'String', - credentialItem: false -}, { - identifier: 'claim-cvc:Type.organizationId-v1', - version: '1', - type: 'String', - credentialItem: false -}, { - identifier: 'claim-cvc:Type.organization-v1', - version: '1', - type: { - properties: [{ - name: 'name', - type: 'claim-cvc:Type.organizationName-v1' - }, { - name: 'id', - type: 'claim-cvc:Type.organizationId-v1' - }] - }, - required: ['name'], - credentialItem: false -}, { - identifier: 'claim-cvc:Type.patient-v1', - version: '1', - type: { - properties: [{ - name: 'fullName', - type: 'cvc:Type:fullName' - }, { - name: 'dateOfBirth', - type: 'cvc:Type:date' - }] - }, - required: ['name'], - credentialItem: true -}, { - identifier: 'claim-cvc:Medical.code-v1', - version: '1', - type: { - properties: [{ - name: 'name', - type: 'cvc:Code:name' - }, { - name: 'code', - type: 'cvc:Code:code' - }, { - name: 'codeSystem', - type: 'cvc:Code:codeSystem' - }, { - name: 'codeSystemName', - type: 'cvc:Code:codeSystemName' - }] - }, - required: ['name', 'code'], - credentialItem: false -}, { - identifier: 'claim-cvc:Manufacturer.name-v1', - version: '1', - type: 'String', - credentialItem: false -}, { - identifier: 'claim-cvc:Vaccination.manufacturer-v1', - version: '1', - type: { - properties: [{ - name: 'name', - type: 'claim-cvc:Manufacturer.name-v1' - }, { - name: 'code', - type: 'claim-cvc:Medical.code-v1' - }], - required: ['name'] - }, - credentialItem: false -}, { - identifier: 'claim-cvc:Vaccination.id-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Medical.codes-v1', - version: '1', - type: 'Array', - items: { - type: 'claim-cvc:Medical.code-v1' - }, - credentialItem: false -}, { - identifier: 'claim-cvc:Codes.records-v1', - version: '1', - type: 'Array', - items: { - type: 'claim-cvc:Medical.code-v1' - }, - credentialItem: false -}, { - identifier: 'claim-cvc:Vaccination.record-v1', - version: '1', - type: { - properties: [{ - name: 'vaccinationId', - type: 'claim-cvc:Vaccination.id-v1' - }, { - name: 'dateOfAdministration', - type: 'claim-cvc:Vaccination.date-v1' - }, { - name: 'manufacturer', - type: 'claim-cvc:Vaccination.manufacturer-v1' - }, { - name: 'name', - type: 'claim-cvc:Vaccination.name-v1' - }, { - name: 'detail', - type: 'claim-cvc:Vaccination.recordDetail-v1' - }, { - name: 'organization', - type: 'cvc:Type:organization' - }, { - name: 'codes', - type: 'claim-cvc:Codes.records-v1' - }], - required: ['vaccinationId', 'dateOfAdministration', 'name', 'organization'] - }, - credentialItem: true -}, { - identifier: 'claim-cvc:Vaccination.records-v1', - version: '1', - type: 'Array', - items: { - type: 'claim-cvc:Vaccination.record-v1' - }, - credentialItem: true -}, { - identifier: 'claim-cvc:Test.type-v1', - version: '1', - type: 'String', - credentialItem: false -}, { - identifier: 'claim-cvc:Test.result-v1', - version: '1', - type: 'String', - credentialItem: false -}, { - identifier: 'claim-cvc:Test.id-v1', - version: '1', - type: 'String', - credentialItem: true -}, { - identifier: 'claim-cvc:Test.record-v1', - version: '1', - type: { - properties: [{ - name: 'testId', - type: 'claim-cvc:Test.id-v1' - }, { - name: 'testDate', - type: 'claim-cvc:Test.date-v1' - }, { - name: 'resultDate', - type: 'claim-cvc:Test.date-v1' - }, { - name: 'type', - type: 'claim-cvc:Test.type-v1' - }, { - name: 'result', - type: 'claim-cvc:Test.result-v1' - }, { - name: 'organization', - type: 'claim-cvc:Type.organization-v1' - }, { - name: 'codes', - type: 'claim-cvc:Codes.records-v1' - }], - required: ['testId', 'testDate', 'type', 'result'] - }, - credentialItem: true -}, { - identifier: 'claim-cvc:Test.records-v1', - version: '1', - type: 'Array', - items: { - type: 'claim-cvc:Test.record-v1' - }, - credentialItem: true -}, { - identifier: 'claim-cvc:Medical.covid19-v1', - version: '1', - type: { - properties: [{ - name: 'vaccinations', - type: 'claim-cvc:Vaccination.records-v1' - }, { - name: 'tests', - type: 'claim-cvc:Test.records-v1' - }, { - name: 'patient', - type: 'claim-cvc:Type.patient-v1' - }] - }, - require: ['patient'], - credentialItem: true -}]; - -function transformUcaIdToClaimId(identifier) { - const identifierComponents = identifier.split(':'); - return `claim-cvc:${identifierComponents[1]}.${identifierComponents[2]}-v1`; -} - -function isDefinitionEqual(definition, ucaDefinition) { - return definition.identifier === transformUcaIdToClaimId(ucaDefinition.identifier) || definition.identifier === ucaDefinition.identifier; -} - -ucaDefinitions.forEach(ucaDefinition => { - let found = false; - definitions.some(definition => { - if (isDefinitionEqual(definition, ucaDefinition)) { - found = true; - } - return found; - }); - if (!found) { - definitions.push(ucaDefinition); - } -}); - -module.exports = definitions; \ No newline at end of file +// This exists for backwards compatibility and should be removed +module.exports = []; \ No newline at end of file diff --git a/dist/es/creds/ClaimModel.js b/dist/es/creds/ClaimModel.js index e487743c..f95db266 100644 --- a/dist/es/creds/ClaimModel.js +++ b/dist/es/creds/ClaimModel.js @@ -1,12 +1,14 @@ const _ = require('lodash'); - /** * Transforms a list of UCAs into the claim property of the verifiable cliams */ + + class ClaimModel { constructor(ucas) { _.forEach(ucas, uca => { const rootPropertyName = uca.getClaimRootPropertyName(); + if (!_.isEmpty(rootPropertyName)) { if (!this[rootPropertyName]) { this[rootPropertyName] = {}; @@ -18,6 +20,9 @@ class ClaimModel { } }); } + } -module.exports = { ClaimModel }; \ No newline at end of file +module.exports = { + ClaimModel +}; \ No newline at end of file diff --git a/dist/es/creds/CredentialSignerVerifier.js b/dist/es/creds/CredentialSignerVerifier.js new file mode 100644 index 00000000..861bdeca --- /dev/null +++ b/dist/es/creds/CredentialSignerVerifier.js @@ -0,0 +1,71 @@ +const _ = require('lodash'); + +const { + HDNode, + ECSignature +} = require('bitcoinjs-lib'); + +const SIGNATURE_ALGO = 'ec256k1'; + +class CredentialSignerVerifier { + /** + * Creates a new instance of a CredentialSignerVerifier + * + * @param options.keyPair any instace that implements sign and verify interface + * or + * @param options.prvBase58 bse58 serialized private key + * or for verification only + * @param options.pubBase58 bse58 serialized public key + */ + constructor(options) { + if (_.isEmpty(options.keyPair) && _.isEmpty(options.prvBase58) && _.isEmpty(options.pubBase58)) { + throw new Error('Either a keyPair, prvBase58 or pubBase58(to verify only) is required'); + } + + this.keyPair = options.keyPair || HDNode.fromBase58(options.prvBase58 || options.pubBase58); + } + /** + * Verify is a credential has a valid merkletree signature, using a pinned pubkey + * @param credential + * @returns {*|boolean} + */ + + + isSignatureValid(credential) { + if (_.isEmpty(credential.proof) || _.isEmpty(credential.proof.merkleRoot) || _.isEmpty(credential.proof.merkleRootSignature)) { + throw Error('Invalid Credential Proof Schema'); + } + + try { + const signatureHex = _.get(credential, 'proof.merkleRootSignature.signature'); + + const signature = signatureHex ? ECSignature.fromDER(Buffer.from(signatureHex, 'hex')) : null; + + const merkleRoot = _.get(credential, 'proof.merkleRoot'); + + return signature && merkleRoot ? this.keyPair.verify(Buffer.from(merkleRoot, 'hex'), signature) : false; + } catch (error) { + // verify throws in must cases but we want to return false + return false; + } + } + /** + * Create a merkleRootSignature object by signing with a pinned private key + * @param proof + * @returns {{signature, pubBase58: *, algo: string}} + */ + + + sign(proof) { + const hash = Buffer.from(proof.merkleRoot, 'hex'); + const signature = this.keyPair.sign(hash); + return { + algo: SIGNATURE_ALGO, + pubBase58: this.keyPair.neutered().toBase58(), + signature: signature.toDER().toString('hex') + }; + } + +} + +module.exports = CredentialSignerVerifier; \ No newline at end of file diff --git a/dist/es/creds/CvcMerkleProof.js b/dist/es/creds/CvcMerkleProof.js index d54f9682..c971d02a 100644 --- a/dist/es/creds/CvcMerkleProof.js +++ b/dist/es/creds/CvcMerkleProof.js @@ -1,60 +1,87 @@ const _ = require('lodash'); + const MerkleTools = require('merkle-tools'); -const { sha256 } = require('../lib/crypto'); -const { Claim } = require('../claim/Claim'); -const { services } = require('../services'); +const { + sha256 +} = require('../lib/crypto'); + +const { + Claim +} = require('../claim/Claim'); +const { + services +} = require('../services'); /** * Transforms a list of UCAs into the signature property of the verifiable claims - */ + + class CvcMerkleProof { static get PADDING_INCREMENTS() { return 16; } - constructor(ucas) { + constructor(ucas, credentialSigner = null) { const withRandomUcas = CvcMerkleProof.padTree(ucas); this.type = 'CvcMerkleProof2018'; this.merkleRoot = null; this.anchor = 'TBD (Civic Blockchain Attestation)'; this.leaves = CvcMerkleProof.getAllAttestableValue(withRandomUcas); - this.buildMerkleTree(); + this.buildMerkleTree(credentialSigner); + this.granted = null; } - buildMerkleTree() { + buildMerkleTree(credentialSigner = null) { const merkleTools = new MerkleTools(); + const hashes = _.map(this.leaves, n => sha256(n.value)); + merkleTools.addLeaves(hashes); merkleTools.makeTree(); + _.forEach(hashes, (hash, idx) => { this.leaves[idx].targetHash = hash; this.leaves[idx].node = merkleTools.getProof(idx); }); + this.leaves = _.filter(this.leaves, el => !(el.identifier === 'cvc:Random:node')); this.merkleRoot = merkleTools.getMerkleRoot().toString('hex'); + + if (credentialSigner) { + this.merkleRootSignature = credentialSigner.sign(this); + } } static padTree(nodes) { const currentLength = nodes.length; const targetLength = currentLength < CvcMerkleProof.PADDING_INCREMENTS ? CvcMerkleProof.PADDING_INCREMENTS : _.ceil(currentLength / CvcMerkleProof.PADDING_INCREMENTS) * CvcMerkleProof.PADDING_INCREMENTS; + const newNodes = _.clone(nodes); + const secureRandom = services.container.SecureRandom; + while (newNodes.length < targetLength) { newNodes.push(new Claim('cvc:Random:node', secureRandom.wordWith(16))); } + return newNodes; } static getAllAttestableValue(ucas) { let values = []; + _.forEach(ucas, uca => { const innerValues = uca.getAttestableValues(); values = _.concat(values, innerValues); }); + return values; } + } -module.exports = { CvcMerkleProof }; \ No newline at end of file +module.exports = { + CvcMerkleProof +}; \ No newline at end of file diff --git a/dist/es/creds/VerifiableCredential.js b/dist/es/creds/VerifiableCredential.js index d632c208..50cf7445 100644 --- a/dist/es/creds/VerifiableCredential.js +++ b/dist/es/creds/VerifiableCredential.js @@ -1,33 +1,72 @@ const _ = require('lodash'); + const validUrl = require('valid-url'); + const sift = require('sift').default; const timestamp = require('unix-timestamp'); + const flatten = require('flat'); + const uuidv4 = require('uuid/v4'); + const MerkleTools = require('merkle-tools'); -const { sha256 } = require('../lib/crypto'); -const { Claim } = require('../claim/Claim'); +const { + sha256 +} = require('../lib/crypto'); + +const { + Claim +} = require('../claim/Claim'); + +const didUtil = require('../lib/did'); const definitions = require('./definitions'); -const { services } = require('../services'); + +const { + services +} = require('../services'); + const time = require('../timeHelper'); -const { CvcMerkleProof } = require('./CvcMerkleProof'); -const { ClaimModel } = require('./ClaimModel'); -// convert a time delta to a timestamp +const { + CvcMerkleProof +} = require('./CvcMerkleProof'); + +const { + ClaimModel +} = require('./ClaimModel'); + +const { + schemaLoader +} = require('../schemas/jsonSchema'); + +const { + parseIdentifier +} = require('../lib/stringUtils'); + +const signerVerifier = require('../lib/signerVerifier'); // convert a time delta to a timestamp + + const convertDeltaToTimestamp = delta => time.applyDeltaToDate(delta).getTime() / 1000; function validIdentifiers() { const vi = _.map(definitions, d => d.identifier); + return vi; } function getClaimsWithFlatKeys(claims) { - const flattenDepth3 = flatten(claims, { maxDepth: 3 }); - const flattenDepth2 = flatten(claims, { maxDepth: 2 }); + const flattenDepth3 = flatten(claims, { + maxDepth: 3 + }); + const flattenDepth2 = flatten(claims, { + maxDepth: 2 + }); + const flattenClaim = _.merge({}, flattenDepth3, flattenDepth2); + return _(flattenClaim).toPairs().sortBy(0).fromPairs().value(); } @@ -38,33 +77,47 @@ function getLeavesClaimPaths(signLeaves) { function verifyLeave(leave, merkleTools, claims, signature, invalidValues, invalidHashs, invalidProofs) { // 1. verify valid targetHashs // 1.1 "leave.value" should be equal claim values - const ucaValue = new Claim(leave.identifier, { attestableValue: leave.value }); + const ucaValue = new Claim(leave.identifier, { + attestableValue: leave.value + }); + + let providedClaimValue = _.get(claims, leave.claimPath); + + if (!providedClaimValue) providedClaimValue = null; + if (ucaValue.type === 'String' || ucaValue.type === 'Number') { - if (ucaValue.value !== _.get(claims, leave.claimPath)) { + if (ucaValue.value !== providedClaimValue) { invalidValues.push(leave.value); } } else if (ucaValue.type === 'Object') { const ucaValueValue = ucaValue.value; - const innerClaimValue = _.get(claims, leave.claimPath); + const innerClaimValue = providedClaimValue; + const claimPathSufix = _.last(_.split(leave.claimPath, '.')); const claimValue = {}; claimValue[claimPathSufix] = innerClaimValue; + const ucaValueKeys = _.keys(ucaValue.value); + _.each(ucaValueKeys, k => { const expectedClaimValue = _.get(claimValue, k); + if (expectedClaimValue && `${_.get(ucaValueValue[k], 'value')}` !== `${expectedClaimValue}`) { invalidValues.push(claimValue[k]); } }); } else if (ucaValue.type === 'Array') { - const innerClaimValue = _.get(claims, leave.claimPath); + const innerClaimValue = providedClaimValue; _.forEach(ucaValue.value, (arrayItem, idx) => { const itemInnerClaimValue = innerClaimValue[idx]; + const ucaValueKeys = _.keys(arrayItem.value); + _.each(ucaValueKeys, k => { const expectedClaimValue = _.get(itemInnerClaimValue, k); + if (expectedClaimValue && `${_.get(arrayItem.value, [k, 'value'])}` !== `${expectedClaimValue}`) { invalidValues.push(itemInnerClaimValue[k]); } @@ -73,28 +126,30 @@ function verifyLeave(leave, merkleTools, claims, signature, invalidValues, inval } else { // Invalid ucaValue.type invalidValues.push(leave.value); - } + } // 1.2 hash(leave.value) should be equal leave.targetHash + - // 1.2 hash(leave.value) should be equal leave.targetHash const hash = sha256(leave.value); - if (hash !== leave.targetHash) invalidHashs.push(leave.targetHash); + if (hash !== leave.targetHash) invalidHashs.push(leave.targetHash); // 2. Validate targetHashs + proofs with merkleRoot - // 2. Validate targetHashs + proofs with merkleRoot const isValidProof = merkleTools.validateProof(leave.node, leave.targetHash, signature.merkleRoot); if (!isValidProof) invalidProofs.push(leave.targetHash); } function validateEvidence(evidenceItem) { const requiredFields = ['type', 'verifier', 'evidenceDocument', 'subjectPresence', 'documentPresence']; + _.forEach(requiredFields, field => { if (!(field in evidenceItem)) { throw new Error(`Evidence ${field} is required`); } - }); - // id property is optional, but if present, SHOULD contain a URL + }); // id property is optional, but if present, SHOULD contain a URL + + if ('id' in evidenceItem && !validUrl.isWebUri(evidenceItem.id)) { throw new Error('Evidence id is not a valid URL'); } + if (!_.isArray(evidenceItem.type)) { throw new Error('Evidence type is not an Array object'); } @@ -114,11 +169,12 @@ function serializeEvidence(evidence) { }; }); } - /** * Transform DSR constraints to sift constraits * @param {*} constraints */ + + function transformConstraint(constraints) { const resultConstraints = []; @@ -126,6 +182,7 @@ function transformConstraint(constraints) { if (!constraint.path) { throw new Error('Malformed contraint: missing PATTH'); } + if (!constraint.is) { throw new Error('Malformed contraint: missing IS'); } @@ -137,35 +194,45 @@ function transformConstraint(constraints) { return resultConstraints; } - /** * Checks if object is a Date Structure (has day, month, year properties) * * @param obj - Structure to test * @return {boolean} */ + + function isDateStructure(obj) { const objKeys = _.keys(obj); + if (objKeys.length !== 3) { // it has more or less keys the (day, month, year) return false; } + return _.includes(objKeys, 'day') && _.includes(objKeys, 'month') && _.includes(objKeys, 'year'); } - /** * Non cryptographically secure verify the Credential * Performs a proofs verification only. * @param credential - A credential object with expirationDate, claim and proof * @return true if verified, false otherwise. */ -function nonCryptographicallySecureVerify(credential) { + + +async function nonCryptographicallySecureVerify(credential) { + await schemaLoader.loadSchemaFromTitle('cvc:Meta:expirationDate'); + await schemaLoader.loadSchemaFromTitle(credential.identifier); + const expiry = _.clone(credential.expirationDate); - const claims = _.clone(credential.claim); + + const claims = _.clone(credential.credentialSubject); + const signature = _.clone(credential.proof); + const signLeaves = _.get(signature, 'leaves'); - let valid = false; + let valid = false; const merkleTools = new MerkleTools(); const claimsWithFlatKeys = getClaimsWithFlatKeys(claims); const leavesClaimPaths = getLeavesClaimPaths(signLeaves); @@ -174,28 +241,34 @@ function nonCryptographicallySecureVerify(credential) { const invalidValues = []; const invalidHashs = []; const invalidProofs = []; - _.forEach(_.keys(claimsWithFlatKeys), claimKey => { + + _.forEach(_.keys(claimsWithFlatKeys).filter(key => key !== 'id'), claimKey => { // check if `claimKey` has a `claimPath` proof - const leaveIdx = _.indexOf(leavesClaimPaths, claimKey); - // if not found + const leaveIdx = _.indexOf(leavesClaimPaths, claimKey); // if not found + + if (leaveIdx === -1) { // .. still test if parent key node may have a `claimPath` proof _.findLastIndex(claimKey, '.'); + const parentClaimKey = claimKey.substring(0, _.lastIndexOf(claimKey, '.')); + if (_.indexOf(leavesClaimPaths, parentClaimKey) > -1) { // if yes, no problem, go to next loop return; - } - // if no, include on invalidClaim array + } // if no, include on invalidClaim array + + invalidClaim.push(claimKey); } else { const leave = signLeaves[leaveIdx]; verifyLeave(leave, merkleTools, claims, signature, invalidValues, invalidHashs, invalidProofs); } - }); + }); // It has to be present Credential expiry even with null value + - // It has to be present Credential expiry even with null value const expiryIdx = _.indexOf(leavesClaimPaths, 'meta.expirationDate'); + if (expiryIdx >= 0) { const expiryLeave = signLeaves[expiryIdx]; const metaClaim = { @@ -206,24 +279,27 @@ function nonCryptographicallySecureVerify(credential) { const totalLengthBefore = invalidValues.length + invalidHashs.length + invalidProofs.length; verifyLeave(expiryLeave, merkleTools, metaClaim, signature, invalidValues, invalidHashs, invalidProofs); const totalLengthAfter = invalidValues.length + invalidHashs.length + invalidProofs.length; + if (totalLengthAfter === totalLengthBefore) { // expiry has always to be string formatted date or null value // if it is null it means it's indefinitely if (expiry !== null) { const now = new Date(); const expiryDate = new Date(expiry); + if (now.getTime() > expiryDate.getTime()) { invalidExpiry.push(expiry); } } } } + if (_.isEmpty(invalidClaim) && _.isEmpty(invalidValues) && _.isEmpty(invalidHashs) && _.isEmpty(invalidProofs) && _.isEmpty(invalidExpiry)) { valid = true; } + return valid; } - /** * Cryptographically secure verify the Credential. * Performs a non cryptographically secure verification, attestation check and signature validation. @@ -232,8 +308,12 @@ function nonCryptographicallySecureVerify(credential) { * @param verifySignatureFunc - Async method to verify a credential signature * @return true if verified, false otherwise. */ + + async function cryptographicallySecureVerify(credential, verifyAttestationFunc, verifySignatureFunc) { - if (!nonCryptographicallySecureVerify(credential)) { + const nonCryptographicallyVerified = await nonCryptographicallySecureVerify(credential); + + if (!nonCryptographicallyVerified) { return false; } @@ -249,106 +329,99 @@ async function cryptographicallySecureVerify(credential, verifyAttestationFunc, return true; } - /** * Verify if a credential was granted for a specific requester and requestId. * @param credential - A credential object with expirationDate, claim and proof * @return true if verified, false otherwise. */ -function requesterGrantVerify(credential, requesterId, requestId, keyName) { + + +async function requesterGrantVerify(credential, requesterId, requestId, keyName) { const label = _.get(credential.proof, 'anchor.subject.label'); + const anchorPubKey = _.get(credential.proof, 'anchor.subject.pub'); + const anchorData = _.get(credential.proof, 'anchor.subject.data'); - if (_.isEmpty(credential.granted) || _.isEmpty(label) || _.isEmpty(anchorPubKey)) { + if (_.isEmpty(credential.proof.granted) || _.isEmpty(label) || _.isEmpty(anchorPubKey)) { return false; } const stringToHash = `${label}${anchorData}${requesterId}${requestId}`; const hexHash = sha256(stringToHash); - const cryptoManager = services.container.CryptoManager; - let verifyKey = keyName; + if (_.isEmpty(verifyKey)) { if (!_.isFunction(cryptoManager.installKey)) { throw new Error('CryptoManager does not support installKey, please use a `keyName` instead.'); } + verifyKey = `TEMP_KEY_NAME_${new Date().getTime()}`; cryptoManager.installKey(verifyKey, anchorPubKey); } - return cryptoManager.verify(verifyKey, hexHash, credential.granted); + return cryptoManager.verify(verifyKey, hexHash, credential.proof.granted); } - /** * Trasnform {day, month, year } to Unix Date * * @param obj {day, month, year } * @return {number} an unix-timestamp in seconds */ + + function transformDate(obj) { return new Date(obj.year, obj.month - 1, obj.day).getTime() / 1000; } const VERIFY_LEVELS = { - INVALID: -1, // Verifies if the VC structure and/or signature proofs is not valid, or credential is expired - PROOFS: 0, // Verifies if the VC structure and/or signature proofs are valid, including the expiry - ANCHOR: 1, // Verifies if the VC Attestation Anchor structure is valid - GRANTED: 2, // Verifies if the owner granted the VC usage for a specific request + INVALID: -1, + // Verifies if the VC structure and/or signature proofs is not valid, or credential is expired + PROOFS: 0, + // Verifies if the VC structure and/or signature proofs are valid, including the expiry + ANCHOR: 1, + // Verifies if the VC Attestation Anchor structure is valid + GRANTED: 2, + // Verifies if the owner granted the VC usage for a specific request BLOCKCHAIN: 3 // Verifies if the VC Attestation is valid on the blockchain -}; +}; /** * Throws exception if the definition has missing required claims * @param {*} definition - the credential definition * @param {*} ucas - the list of ucas */ + function verifyRequiredClaims(definition, ucas) { if (!_.isEmpty(definition.required)) { const identifiers = ucas.map(uca => uca.identifier); - const missings = _.difference(definition.required, identifiers); - if (!_.isEmpty(missings)) { - throw new Error(`Missing required claim(s): ${_.join(missings, ', ')}`); - } - } -} -/** - * Throws exception if the definition has missing required claims - * @param {*} definition - the credential definition - * @param {*} verifiableCredentialJSON - the verifiable credential JSON - */ -function verifyRequiredClaimsFromJSON(definition, verifiableCredentialJSON) { - const leaves = _.get(verifiableCredentialJSON, 'proof.leaves'); - - if (!_.isEmpty(definition.required) && leaves) { - const identifiers = leaves.map(leave => leave.identifier); const missings = _.difference(definition.required, identifiers); + if (!_.isEmpty(missings)) { throw new Error(`Missing required claim(s): ${_.join(missings, ', ')}`); } } } - /** * Retrieves the credential definition * @param {string} identifier - credential identifier * @param {*} [version] - definition version */ + + function getCredentialDefinition(identifier, version) { - let definition; - if (version) { - definition = _.find(definitions, { identifier, version: `${version}` }); - } else { - definition = _.find(definitions, { identifier }); - } + const definition = _.find(definitions, { + identifier + }); + if (!definition) { throw new Error(`Credential definition for ${identifier} v${version} not found`); } + return definition; } - /** * Creates a new Verifiable Credential based on an well-known identifier and it's claims dependencies * @param {*} identifier @@ -357,7 +430,11 @@ function getCredentialDefinition(identifier, version) { * @param {*} version * @param {*} [evidence] */ -function VerifiableCredentialBaseConstructor(identifier, issuer, expiryIn, ucas, version, evidence) { + + +function VerifiableCredentialBaseConstructor(identifier, issuer, expiryIn, subject, ucas, evidence, signerOptions) { + const parsedIdentifier = parseIdentifier(identifier); + const version = parsedIdentifier ? parsedIdentifier[4] : '1'; this.id = uuidv4(); this.issuer = issuer; const issuerUCA = new Claim('cvc:Meta:issuer', this.issuer); @@ -366,58 +443,65 @@ function VerifiableCredentialBaseConstructor(identifier, issuer, expiryIn, ucas, this.identifier = identifier; this.expirationDate = expiryIn ? timestamp.toDate(timestamp.now(expiryIn)).toISOString() : null; const expiryUCA = new Claim('cvc:Meta:expirationDate', this.expirationDate ? this.expirationDate : 'null'); - const proofUCAs = expiryUCA ? _.concat(ucas, issuerUCA, issuanceDateUCA, expiryUCA) : _.concat(ucas, issuerUCA, issuanceDateUCA); if (!_.includes(validIdentifiers(), identifier)) { throw new Error(`${identifier} is not defined`); } - const definition = getCredentialDefinition(identifier, version); - this.version = `${version}` || definition.version; - this.type = ['Credential', identifier]; + const definition = getCredentialDefinition(identifier, version); // this.version = `${version}` || definition.version; + + this.type = ['VerifiableCredential', 'IdentityCredential']; this.transient = definition.transient || false; if (evidence) { this.evidence = serializeEvidence(evidence); } - // ucas can be empty here if it is been constructed from JSON + this.credentialSubject = { + id: subject + }; // ucas can be empty here if it is been constructed from JSON + if (!_.isEmpty(ucas)) { verifyRequiredClaims(definition, ucas); - this.claim = new ClaimModel(ucas); - this.proof = new CvcMerkleProof(proofUCAs); + this.credentialSubject = { ...this.credentialSubject, + ...new ClaimModel(ucas) + }; + this.proof = new CvcMerkleProof(proofUCAs, signerOptions ? signerOptions.signer : null); + if (!_.isEmpty(definition.excludes)) { const removed = _.remove(this.proof.leaves, el => _.includes(definition.excludes, el.identifier)); + _.forEach(removed, r => { - _.unset(this.claim, r.claimPath); + _.unset(this.credentialSubject, r.claimPath); }); } - // The VC Grantted session (see .grantUsageFor) - this.granted = null; } - /** * Returns the global identifier of the Credential */ - this.getGlobalIdentifier = () => `credential-${this.identifier}-${this.version}`; + + this.getGlobalIdentifier = () => `credential-${this.identifier}-${version}`; /** * Creates a filtered credential exposing only the requested claims * @param {*} requestedClaims */ + + this.filter = requestedClaims => { const filtered = _.cloneDeep(this); + _.remove(filtered.proof.leaves, el => !_.includes(requestedClaims, el.identifier)); - filtered.claim = {}; + filtered.credentialSubject = {}; + _.forEach(filtered.proof.leaves, el => { - _.set(filtered.claim, el.claimPath, _.get(this.claim, el.claimPath)); + _.set(filtered.credentialSubject, el.claimPath, _.get(this.credentialSubject, el.claimPath)); }); return filtered; }; - /** * Request that this credential MerkleRoot is anchored on the Blockchain. * This will return a _temporary_ anchor meaning that the blockchain entry is still not confirmed. @@ -436,6 +520,8 @@ function VerifiableCredentialBaseConstructor(identifier, issuer, expiryIn, ucas, * @returns the json object containing the whole anchor attestation * */ + + this.requestAnchor = async options => { if (this.transient) { // If credential is transient no Blockchain attestation is issued @@ -450,21 +536,24 @@ function VerifiableCredentialBaseConstructor(identifier, issuer, expiryIn, ucas, } const anchorService = services.container.AnchorService; + const updatedOption = _.merge({}, options, { subject: { label: this.identifier, data: this.proof.merkleRoot } }); + const anchor = await anchorService.anchor(updatedOption); this.proof.anchor = anchor; return this; }; - /** * Trys to renew the current anchor. replecinf the _temporary_ anchor for a _permanent_ one, * already confirmed on the blockchain. */ + + this.updateAnchor = async () => { // If credential is transient no Blockchain attestation is issued if (this.transient) { @@ -478,53 +567,70 @@ function VerifiableCredentialBaseConstructor(identifier, issuer, expiryIn, ucas, }; return this; } + const anchorService = services.container.AnchorService; const anchor = await anchorService.update(this.proof.anchor); this.proof.anchor = anchor; return this; }; - /** * Iterate over all leaves and see if their proofs are valid * @returns {boolean} */ - this.verifyProofs = () => nonCryptographicallySecureVerify(this); + + this.verifyProofs = () => nonCryptographicallySecureVerify(this); /** * Verify the Credential and return a verification level. * @return Any of VC.VERIFY_LEVELS + * @deprecated */ - this.verify = (higherVerifyLevel, options) => { - const { requestorId, requestId, keyName } = options || {}; + + + this.verify = async (higherVerifyLevel, options) => { + const { + requestorId, + requestId, + keyName + } = options || {}; const hVerifyLevel = !_.isNil(higherVerifyLevel) ? higherVerifyLevel : VERIFY_LEVELS.GRANTED; - let verifiedlevel = VERIFY_LEVELS.INVALID; + let verifiedlevel = VERIFY_LEVELS.INVALID; // Test next level - // Test next level - if (verifiedlevel === VERIFY_LEVELS.INVALID && hVerifyLevel >= VERIFY_LEVELS.PROOFS && this.verifyProofs()) verifiedlevel = VERIFY_LEVELS.PROOFS; + if (verifiedlevel === VERIFY_LEVELS.INVALID && hVerifyLevel >= VERIFY_LEVELS.PROOFS && (await this.verifyProofs())) verifiedlevel = VERIFY_LEVELS.PROOFS; // Test next level - // Test next level - if (verifiedlevel === VERIFY_LEVELS.PROOFS && hVerifyLevel >= VERIFY_LEVELS.ANCHOR && this.verifyAttestation()) verifiedlevel = VERIFY_LEVELS.ANCHOR; + if (verifiedlevel === VERIFY_LEVELS.PROOFS && hVerifyLevel >= VERIFY_LEVELS.ANCHOR && this.verifyAttestation()) verifiedlevel = VERIFY_LEVELS.ANCHOR; // Test next level - // Test next level if (verifiedlevel === VERIFY_LEVELS.ANCHOR && hVerifyLevel >= VERIFY_LEVELS.GRANTED && this.verifyGrant(requestorId, requestId, keyName)) verifiedlevel = VERIFY_LEVELS.GRANTED; - return verifiedlevel; }; - /** * This method checks if the signature matches for the root of the Merkle Tree * @return true or false for the validation */ - this.verifySignature = pinnedPubKey => { + + + this.verifyAnchorSignature = pinnedPubKey => { if (this.proof.anchor.type === 'transient') { return true; } + return services.container.AnchorService.verifySignature(this.proof, pinnedPubKey); }; + /** + * This methods check the stand alone merkletreeSiganture + * return true or false for the validation + */ + + this.verifyMerkletreeSignature = async () => { + const verifier = await signerVerifier.verifier(this.issuer, this.proof.merkleRootSignature.verificationMethod); + return verifier.verify(this); + }; /** * This method checks that the attestation / anchor exists on the BC */ + + this.verifyAttestation = async () => { // Don't check attestation for credentials that are never attested on blockchain if (this.proof.anchor.type === 'transient' || this.proof.anchor.network === 'dummynet') { @@ -533,51 +639,59 @@ function VerifiableCredentialBaseConstructor(identifier, issuer, expiryIn, ucas, return services.container.AnchorService.verifyAttestation(this.proof); }; - /** * This method will revoke the attestation on the chain * @returns {Promise|void>} */ + + this.revokeAttestation = async () => { if (this.proof.type === 'transient') { return; - } - // eslint-disable-next-line consistent-return + } // eslint-disable-next-line consistent-return + + return services.container.AnchorService.revokeAttestation(this.proof); }; - /** * This method will check on the chain the balance of the transaction and if it's still unspent, than it's not revoked * @returns {Promise|void>} */ + + this.isRevoked = async () => { if (this.proof.type === 'transient') { return false; } + return services.container.AnchorService.isRevoked(this.proof); }; const convertTimestampIfString = obj => _.isString(obj) ? convertDeltaToTimestamp(obj) : obj; this.isMatch = constraints => { - const claims = _.cloneDeep(this.claim); + const claims = _.cloneDeep(this.credentialSubject); + const siftCompatibleConstraints = transformConstraint(constraints); const claimsMatchConstraint = constraint => { const path = _.keys(constraint)[0]; + const pathValue = _.get(claims, path); + if (isDateStructure(pathValue)) { - _.set(claims, path, transformDate(pathValue)); - // transforms delta values like "-18y" to a proper timestamp + _.set(claims, path, transformDate(pathValue)); // transforms delta values like "-18y" to a proper timestamp + + _.set(constraint, path, _.mapValues(constraint[path], convertTimestampIfString)); - } - // The Constraints are ANDed here - if one is false, the entire + } // The Constraints are ANDed here - if one is false, the entire + + return sift(constraint)([claims]); }; return siftCompatibleConstraints.reduce((matchesAllConstraints, nextConstraint) => matchesAllConstraints && claimsMatchConstraint(nextConstraint), true); }; - /** * Updates the credential with a "granted" token based on the requestorId and a unique requestId (a nonce) that * can be verified later using .verify() function. @@ -588,70 +702,103 @@ function VerifiableCredentialBaseConstructor(identifier, issuer, expiryIn, ucas, * @param {string} option.keyName - A keyName - if CryptoManager is been used. * @param {string} option.pvtKey - A pvtKey in base58 format (default impl). */ - this.grantUsageFor = (requestorId, requestId, { keyName, pvtKey }) => { + + + this.grantUsageFor = (requestorId, requestId, { + keyName, + pvtKey + }) => { if (_.isEmpty(_.get(this.proof, 'anchor.subject.label')) || _.isEmpty(_.get(this.proof, 'anchor.subject.data'))) { throw new Error('Invalid credential attestation/anchor'); } - if (!this.verifySignature()) { + + if (!this.verifyAnchorSignature()) { throw new Error('Invalid credential attestation/anchor signature'); } + if (!requestorId || !requestId || !(keyName || pvtKey)) { throw new Error('Missing required parameter: requestorId, requestId or key'); - } - // eslint-disable-next-line max-len + } // eslint-disable-next-line max-len + + const stringToHash = `${this.proof.anchor.subject.label}${this.proof.anchor.subject.data}${requestorId}${requestId}`; const hexHash = sha256(stringToHash); - const cryptoManager = services.container.CryptoManager; - let signKey = keyName; + if (pvtKey) { if (!_.isFunction(cryptoManager.installKey)) { throw new Error('You provide a `pvtKey` but the CryptoManager does not support it, use a `keyName` instead.'); } + signKey = `TEMP_KEY_NAME_${new Date().getTime()}`; cryptoManager.installKey(signKey, pvtKey); } const hexSign = cryptoManager.sign(signKey, hexHash); - this.granted = hexSign; + this.proof.granted = hexSign; }; + /** + * Serializes the VerifiableCredential to a JSON string + * @param space The number of spaces to indent the JSON with + */ + + this.toJSON = () => { + const obj = _.pick(this, ['id', 'identifier', 'issuer', 'issuanceDate', 'expirationDate', 'type', 'credentialSubject', 'proof']); // Remove undefined/null values + // eslint-disable-next-line no-restricted-syntax + + + for (const k in obj) { + if (obj[k] === null || obj[k] === undefined) { + delete obj[k]; + } + } + + return { + '@context': ['https://www.w3.org/2018/credentials/v1', `https://www.identity.com/credentials/v${version}`], + ...obj + }; + }; /** * @param {} requestorId * @param {} requestId * @param {} [keyName] */ + + this.verifyGrant = (requesterId, requestId, keyName) => requesterGrantVerify(this, requesterId, requestId, keyName); return this; } - /** * CREDENTIAL_META_FIELDS - Array with meta fields of a credential */ -const CREDENTIAL_META_FIELDS = ['id', 'identifier', 'issuer', 'issuanceDate', 'expirationDate', 'version', 'type']; + +const CREDENTIAL_META_FIELDS = ['id', 'identifier', 'issuer', 'issuanceDate', 'expirationDate', 'type']; /** * * @param {*} vc */ -const getCredentialMeta = vc => _.pick(vc, CREDENTIAL_META_FIELDS); +const getCredentialMeta = vc => _.pick(vc, CREDENTIAL_META_FIELDS); /** * Sift constraints to throw errors for constraints missing IS * @param {*} constraintsMeta * @param Array */ + + function transformMetaConstraint(constraintsMeta) { - const resultConstraints = []; + const resultConstraints = []; // handle special field constraints.meta.credential - // handle special field constraints.meta.credential const constraintsMetaKeys = _.keys(constraintsMeta.meta); + _.forEach(constraintsMetaKeys, constraintKey => { const constraint = constraintsMeta.meta[constraintKey]; - const siftConstraint = {}; - // handle special field constraints.meta.credential + const siftConstraint = {}; // handle special field constraints.meta.credential + if (constraintKey === 'credential') { siftConstraint.identifier = constraint; } else if (constraint.is) { @@ -659,11 +806,12 @@ function transformMetaConstraint(constraintsMeta) { } else { throw new Error(`Malformed meta constraint "${constraintKey}": missing the IS`); } + resultConstraints.push(siftConstraint); }); + return resultConstraints; } - /** * isMatchCredentialMeta * @param {*} credentialMeta An Object contains only VC meta fields. Other object keys will be ignored. @@ -677,9 +825,10 @@ function transformMetaConstraint(constraintsMeta) { * // } * @returns boolean */ + + const isMatchCredentialMeta = (credentialMeta, constraintsMeta) => { const siftCompatibleConstraints = transformMetaConstraint(constraintsMeta); - if (_.isEmpty(siftCompatibleConstraints)) return false; const credentialMetaMatchesConstraint = constraint => sift(constraint)([credentialMeta]); @@ -690,46 +839,107 @@ const isMatchCredentialMeta = (credentialMeta, constraintsMeta) => { VerifiableCredentialBaseConstructor.CREDENTIAL_META_FIELDS = CREDENTIAL_META_FIELDS; VerifiableCredentialBaseConstructor.getCredentialMeta = getCredentialMeta; VerifiableCredentialBaseConstructor.isMatchCredentialMeta = isMatchCredentialMeta; +/** + * Creates a Verifiable Credential + * + * @param identifier The identifier for the VC (e.g. credential-cvc:Identity-v1) + * @param issuerDid The issuer DID + * @param expiryIn The credential expiry date (nullable) + * @param subject The subject DID + * @param ucas An array of UCAs + * @param evidence The evidence for the credential + * @param signerOptions Signer options: + * @param signerOptions.verificationMethod The verificationMethod for the signing key + * @param signerOptions.keypair The keypair to sign with + * or + * @param signerOptions.privateKey The private key to sign with + * or + * @param signerOptions.signer An object implementing a `sign(CvcMerkleProof)` method + */ + +VerifiableCredentialBaseConstructor.create = async (identifier, issuerDid, expiryIn, subject, ucas, evidence, signerOptions = null, validate = true) => { + // Load the schema and it's references from a source to be used for validation and defining the schema definitions + await schemaLoader.loadSchemaFromTitle(identifier); // Load the meta schema's from a source + + await schemaLoader.loadSchemaFromTitle('cvc:Meta:issuer'); + await schemaLoader.loadSchemaFromTitle('cvc:Meta:issuanceDate'); + await schemaLoader.loadSchemaFromTitle('cvc:Meta:expirationDate'); + await schemaLoader.loadSchemaFromTitle('cvc:Random:node'); + + if (signerOptions) { + const canSignForIssuer = await didUtil.canSign(issuerDid, signerOptions.verificationMethod); + + if (!canSignForIssuer) { + throw new Error(`The verificationMethod ${signerOptions.verificationMethod} is not allowed to sign for ${issuerDid}`); + } // eslint-disable-next-line no-param-reassign + + signerOptions.signer = await signerVerifier.signer(signerOptions); + } + + const vc = new VerifiableCredentialBaseConstructor(identifier, issuerDid, expiryIn, subject, ucas, evidence, signerOptions); + + if (validate) { + await schemaLoader.validateSchema(identifier, vc.toJSON()); + } + + return vc; +}; /** * Factory function that creates a new Verifiable Credential based on a JSON object * @param {*} verifiableCredentialJSON * @returns VerifiableCredentialBaseConstructor */ -VerifiableCredentialBaseConstructor.fromJSON = verifiableCredentialJSON => { - const definition = getCredentialDefinition(verifiableCredentialJSON.identifier, verifiableCredentialJSON.version); - verifyRequiredClaimsFromJSON(definition, verifiableCredentialJSON); - const newObj = new VerifiableCredentialBaseConstructor(verifiableCredentialJSON.identifier, verifiableCredentialJSON.issuer); +VerifiableCredentialBaseConstructor.fromJSON = async (verifiableCredentialJSON, partialPresentation = false) => { + await schemaLoader.loadSchemaFromTitle(verifiableCredentialJSON.identifier); + + if (!partialPresentation) { + await schemaLoader.validateSchema(verifiableCredentialJSON.identifier, verifiableCredentialJSON); + } + + const newObj = await VerifiableCredentialBaseConstructor.create(verifiableCredentialJSON.identifier, verifiableCredentialJSON.issuer); newObj.id = _.clone(verifiableCredentialJSON.id); newObj.issuanceDate = _.clone(verifiableCredentialJSON.issuanceDate); newObj.expirationDate = _.clone(verifiableCredentialJSON.expirationDate); newObj.identifier = _.clone(verifiableCredentialJSON.identifier); - newObj.version = _.clone(verifiableCredentialJSON.version); newObj.type = _.cloneDeep(verifiableCredentialJSON.type); - newObj.claim = _.cloneDeep(verifiableCredentialJSON.claim); + newObj.credentialSubject = _.cloneDeep(verifiableCredentialJSON.credentialSubject); newObj.proof = _.cloneDeep(verifiableCredentialJSON.proof); - newObj.granted = _.clone(verifiableCredentialJSON.granted) || null; return newObj; }; - /** * List all properties of a Verifiable Credential */ -VerifiableCredentialBaseConstructor.getAllProperties = identifier => { - const vcDefinition = _.find(definitions, { identifier }); + + +VerifiableCredentialBaseConstructor.getAllProperties = async identifier => { + await schemaLoader.loadSchemaFromTitle(identifier); + + const vcDefinition = _.find(definitions, { + identifier + }); + if (vcDefinition) { - const allProperties = []; - _.forEach(vcDefinition.depends, ucaIdentifier => { - allProperties.push(...Claim.getAllProperties(ucaIdentifier)); - }); - const excludesProperties = []; - _.forEach(vcDefinition.excludes, ucaIdentifier => { - excludesProperties.push(...Claim.getAllProperties(ucaIdentifier)); - }); + const allProperties = await vcDefinition.depends.reduce(async (prev, definition) => { + const prevProps = await prev; + const claimProps = await Claim.getAllProperties(definition); + return [...prevProps, ...claimProps]; + }, Promise.resolve([])); + let excludesProperties = []; + + if (vcDefinition.excludes) { + excludesProperties = await vcDefinition.excludes.reduce(async (prev, definition) => { + const prevProps = await prev; + const claimProps = await Claim.getAllProperties(definition); + return [...prevProps, ...claimProps]; + }, Promise.resolve([])); + } + return _.difference(allProperties, excludesProperties); } + return null; }; @@ -737,5 +947,4 @@ VerifiableCredentialBaseConstructor.VERIFY_LEVELS = VERIFY_LEVELS; VerifiableCredentialBaseConstructor.nonCryptographicallySecureVerify = nonCryptographicallySecureVerify; VerifiableCredentialBaseConstructor.cryptographicallySecureVerify = cryptographicallySecureVerify; VerifiableCredentialBaseConstructor.requesterGrantVerify = requesterGrantVerify; - module.exports = VerifiableCredentialBaseConstructor; \ No newline at end of file diff --git a/dist/es/creds/VerifiableCredentialProxy.js b/dist/es/creds/VerifiableCredentialProxy.js new file mode 100644 index 00000000..e29fc005 --- /dev/null +++ b/dist/es/creds/VerifiableCredentialProxy.js @@ -0,0 +1,192 @@ +const _ = require('lodash'); + +const VerifiableCredential = require('./VerifiableCredential'); + +const { + schemaLoader +} = require('../schemas/jsonSchema'); + +const CredentialSignerVerifier = require('./CredentialSignerVerifier'); + +const definitions = schemaLoader.credentialDefinitions; +/** + * Retrieves the credential definition + * @param {string} identifier - credential identifier + * @param {*} [version] - definition version + */ + +function getCredentialDefinition(identifier, version) { + const definition = _.find(definitions, { + identifier + }); + + if (!definition) { + throw new Error(`Credential definition for ${identifier} v${version} not found`); + } + + return definition; +} +/** + * Throws exception if the definition has missing required claims + * @param {*} definition - the credential definition + * @param {*} verifiableCredentialJSON - the verifiable credential JSON + */ + + +function verifyRequiredClaimsFromJSON(definition, verifiableCredentialJSON) { + const leaves = _.get(verifiableCredentialJSON, 'proof.leaves'); + + if (!_.isEmpty(definition.required) && leaves) { + const identifiers = leaves.map(leave => leave.identifier); + + const missings = _.difference(definition.required, identifiers); + + if (!_.isEmpty(missings)) { + throw new Error(`Missing required claim(s): ${_.join(missings, ', ')}`); + } + } +} + +class VerifiableCredentialProxy extends VerifiableCredential { + get claim() { + return this.credentialSubject; + } + + get granted() { + return this.proof && this.proof.granted ? this.proof.granted : null; + } + + set granted(granted) { + this.proof.granted = granted; + } + + constructor(identifier, issuer, expiryIn, ucas, version, evidence, signerVerifier = null) { + super(identifier, issuer, expiryIn, null, ucas, evidence, signerVerifier); + this.version = version; + /** + * Returns the old format VC when converting to JSON + */ + + this.toJSON = () => { + const obj = { + id: _.clone(this.id), + identifier: _.clone(this.identifier), + issuer: _.clone(this.issuer), + issuanceDate: _.clone(this.issuanceDate), + expirationDate: _.clone(this.expirationDate), + version: _.clone(this.version), + type: ['Credential', this.identifier], + claim: _.clone(this.credentialSubject), + proof: _.clone(this.proof) + }; + if (obj.claim) delete obj.claim.id; + return obj; + }; // maintains the old verification process for the older VC format + + + this.verifyMerkletreeSignature = pubBase58 => { + if (_.isEmpty(pubBase58)) return false; + const verifier = new CredentialSignerVerifier({ + pubBase58 + }); + return verifier.isSignatureValid(this); + }; + } + +} + +VerifiableCredentialProxy.create = async (identifier, issuer, expiryIn, ucas, version, evidence, signerVerifier = null) => { + // Load the schema and it's references from a source to be used for validation and defining the schema definitions + const schema = await schemaLoader.loadSchemaFromTitle(identifier); // Wrap the old signer verifier for backwards compatibility + + let signer; + + if (signerVerifier) { + signer = { + signer: signerVerifier + }; + } // If it has a credentialSubject, use the new VC format + + + if (schema && schema.properties.credentialSubject) { + return VerifiableCredential.create(identifier, issuer, expiryIn, '', ucas, evidence, signer); + } // Load the meta schema's from a source + + + await schemaLoader.loadSchemaFromTitle('cvc:Meta:issuer'); + await schemaLoader.loadSchemaFromTitle('cvc:Meta:issuanceDate'); + await schemaLoader.loadSchemaFromTitle('cvc:Meta:expirationDate'); + await schemaLoader.loadSchemaFromTitle('cvc:Random:node'); + return new VerifiableCredentialProxy(identifier, issuer, expiryIn, ucas, version, evidence, signer); +}; +/** + * Factory function that creates a new Verifiable Credential based on a JSON object. + * + * This proxy function ensures that the VC is converted into the new format. + * @param {*} verifiableCredentialJSON + * @returns VerifiableCredentialBaseConstructor + */ + + +VerifiableCredentialProxy.fromJSON = async (verifiableCredentialJSON, partialPresentation = false) => { + const schema = await schemaLoader.loadSchemaFromTitle(verifiableCredentialJSON.identifier); + const properties = await schemaLoader.flattenCredentialSchemaProperties(schema); + + if (properties.credentialSubject) { + return VerifiableCredential.fromJSON(verifiableCredentialJSON); + } + + const newObj = await VerifiableCredentialProxy.create(verifiableCredentialJSON.identifier, verifiableCredentialJSON.issuer); + newObj.id = _.clone(verifiableCredentialJSON.id); + newObj.issuanceDate = _.clone(verifiableCredentialJSON.issuanceDate); + newObj.expirationDate = _.clone(verifiableCredentialJSON.expirationDate); + newObj.identifier = _.clone(verifiableCredentialJSON.identifier); + newObj.version = _.clone(verifiableCredentialJSON.version); + newObj.type = ['VerifiableCredential', 'IdentityCredential']; + newObj.credentialSubject = _.cloneDeep(verifiableCredentialJSON.claim); + newObj.proof = _.cloneDeep(verifiableCredentialJSON.proof); + + if (!partialPresentation) { + const definition = getCredentialDefinition(verifiableCredentialJSON.identifier, verifiableCredentialJSON.version); + verifyRequiredClaimsFromJSON(definition, verifiableCredentialJSON); + } + + return newObj; +}; +/** + * Non cryptographically secure verify the Credential + * Performs a proofs verification only. + * + * This proxy function ensures that if the VC is provided as a value object, it is correctly converted + * @param credential - A credential object with expirationDate, claim and proof + * @return true if verified, false otherwise. + */ + + +VerifiableCredentialProxy.nonCryptographicallySecureVerify = async credential => { + const vc = await VerifiableCredentialProxy.fromJSON(credential); + return VerifiableCredential.nonCryptographicallySecureVerify(vc); +}; +/** + * Cryptographically secure verify the Credential. + * Performs a non cryptographically secure verification, attestation check and signature validation. + * + * This proxy function ensures that if the VC is provided as a value object, it is correctly converted + * @param credential - A credential object with expirationDate, claim and proof + * @param verifyAttestationFunc - Async method to verify a credential attestation + * @param verifySignatureFunc - Async method to verify a credential signature + * @return true if verified, false otherwise. + */ + + +VerifiableCredentialProxy.cryptographicallySecureVerify = async (credential, verifyAttestationFunc, verifySignatureFunc) => { + const vc = await VerifiableCredentialProxy.fromJSON(credential); + return VerifiableCredential.cryptographicallySecureVerify(vc, verifyAttestationFunc, verifySignatureFunc); +}; + +VerifiableCredentialProxy.requesterGrantVerify = async (credential, requesterId, requestId, keyName) => { + const vc = await VerifiableCredentialProxy.fromJSON(credential); + return VerifiableCredential.requesterGrantVerify(vc, requesterId, requestId, keyName); +}; + +module.exports = VerifiableCredentialProxy; \ No newline at end of file diff --git a/dist/es/creds/__mocks__/definitions.js b/dist/es/creds/__mocks__/definitions.js index 7d4291e8..03c1b673 100644 --- a/dist/es/creds/__mocks__/definitions.js +++ b/dist/es/creds/__mocks__/definitions.js @@ -12,5 +12,4 @@ const definitions = [{ depends: ['civ:Identity:name', 'civ:Identity:DateOfBirth'], excludes: ['civ:Identity:name.middle'] }]; - module.exports = definitions; \ No newline at end of file diff --git a/dist/es/creds/definitions.js b/dist/es/creds/definitions.js index a618feb9..81ab88b8 100644 --- a/dist/es/creds/definitions.js +++ b/dist/es/creds/definitions.js @@ -1,66 +1,2 @@ -/** - * name: 'attgenericId', - * name: 'attBaseIdentity', - * name: 'attAddress', - * @type {*[]} - */ -const definitions = [{ - identifier: 'credential-cvc:Email-v1', - version: '1', - depends: ['claim-cvc:Contact.email-v1'] -}, { - identifier: 'credential-cvc:PhoneNumber-v1', - version: '1', - depends: ['claim-cvc:Contact.phoneNumber-v1'] -}, { - identifier: 'credential-cvc:GenericDocumentId-v1', - version: '1', - depends: ['claim-cvc:Document.type-v1', 'claim-cvc:Document.number-v1', 'claim-cvc:Document.name-v1', 'claim-cvc:Document.gender-v1', 'claim-cvc:Document.issueLocation-v1', 'claim-cvc:Document.issueAuthority-v1', 'claim-cvc:Document.issueCountry-v1', 'claim-cvc:Document.placeOfBirth-v1', 'claim-cvc:Document.dateOfBirth-v1', 'claim-cvc:Document.address-v1', 'claim-cvc:Document.properties-v1', 'cvc:Document:image'] -}, { - identifier: 'credential-cvc:IdDocument-v1', - version: '1', - depends: ['claim-cvc:Document.type-v1', 'claim-cvc:Document.number-v1', 'claim-cvc:Document.name-v1', 'claim-cvc:Document.gender-v1', 'claim-cvc:Document.issueCountry-v1', 'claim-cvc:Document.placeOfBirth-v1', 'claim-cvc:Document.dateOfBirth-v1', 'claim-cvc:Document.dateOfExpiry-v1', 'claim-cvc:Document.nationality-v1', 'claim-cvc:Document.evidences-v1'], - required: ['claim-cvc:Document.type-v1', 'claim-cvc:Document.name-v1', 'claim-cvc:Document.dateOfBirth-v1', 'claim-cvc:Document.issueCountry-v1'] -}, { - identifier: 'credential-cvc:IdDocument-v2', - version: '1', - depends: ['claim-cvc:Document.type-v1', 'claim-cvc:Document.number-v1', 'claim-cvc:Document.name-v1', 'claim-cvc:Document.gender-v1', 'claim-cvc:Document.issueCountry-v1', 'claim-cvc:Document.placeOfBirth-v1', 'claim-cvc:Document.dateOfBirth-v1', 'claim-cvc:Document.dateOfExpiry-v1', 'claim-cvc:Document.nationality-v1', 'claim-cvc:Document.evidences-v1'], - required: ['claim-cvc:Document.type-v1', 'claim-cvc:Document.name-v1', 'claim-cvc:Document.dateOfBirth-v1', 'claim-cvc:Document.issueCountry-v1', 'claim-cvc:Document.evidences-v1'] -}, { - identifier: 'credential-cvc:LiveIdDocument-v1', - version: '1', - depends: ['claim-cvc:Document.type-v1', 'claim-cvc:Document.number-v1', 'claim-cvc:Document.name-v1', 'claim-cvc:Document.gender-v1', 'claim-cvc:Document.issueCountry-v1', 'claim-cvc:Document.placeOfBirth-v1', 'claim-cvc:Document.dateOfBirth-v1', 'claim-cvc:Document.dateOfExpiry-v1', 'claim-cvc:Document.nationality-v1', 'claim-cvc:Document.evidences-v1'], - required: ['claim-cvc:Document.type-v1', 'claim-cvc:Document.name-v1', 'claim-cvc:Document.dateOfBirth-v1', 'claim-cvc:Document.issueCountry-v1', 'claim-cvc:Document.evidences-v1'] -}, { - identifier: 'credential-cvc:Address-v1', - version: '1', - depends: ['claim-cvc:Identity.address-v1'] -}, { - identifier: 'credential-cvc:Identity-v1', - version: '1', - depends: ['claim-cvc:Identity.name-v1', 'claim-cvc:Identity.dateOfBirth-v1'] -}, { - identifier: 'credential-cvc:IDVaaS-v1', - version: '1', - depends: ['claim-cvc:Identity.name-v1', 'claim-cvc:Identity.dateOfBirth-v1', 'claim-cvc:Identity.address-v1', 'claim-cvc:Contact.email-v1', 'claim-cvc:SocialSecurity.number-v1', 'claim-cvc:Contact.phoneNumber-v1'] -}, { - identifier: 'credential-cvc:UnverifiedSsn-v1', - version: '1', - transient: true, - depends: ['claim-cvc:SocialSecurity.number-v1'] -}, { - identifier: 'credential-cvc:UnverifiedAddress-v1', - version: '1', - transient: true, - depends: ['claim-cvc:Identity.address-v1'] -}, { - identifier: 'credential-cvc:Covid19-v1', - version: '1', - depends: ['claim-cvc:Medical.covid19-v1'] -}, { - identifier: 'credential-alt:Identity-v1', - version: '1', - depends: ['claim-cvc:Identity.name-v1', 'claim-cvc:Identity.dateOfBirth-v1', 'claim-cvc:Identity.address-v1'] -}]; - -module.exports = definitions; \ No newline at end of file +// This exists for backwards compatibility and should be removed +module.exports = []; \ No newline at end of file diff --git a/dist/es/errors/definitions.js b/dist/es/errors/definitions.js index 70c46d3b..a3552d00 100644 --- a/dist/es/errors/definitions.js +++ b/dist/es/errors/definitions.js @@ -4,9 +4,9 @@ * @enum { string } */ const ErrorCodes = { - // IDV // Manual Review Tool + /** * Reason: Manual Review detected that the provided document is invalid. * Troubleshooting: Make sure the provided document is a valid one. @@ -48,8 +48,8 @@ const ErrorCodes = { * Troubleshooting: Make sure the provided document do fit the requirements. */ ERROR_IDV_MRT_REQUIREMENTS_FAIL: 'error.idv.mrt.requirements.fail', - // Validation + /** * Reason: The IDV detected that the provided document is invalid. * Troubleshooting: Make sure the provided document is a valid one. @@ -180,6 +180,13 @@ const ErrorCodes = { */ ERROR_IDV_CREDENTIAL_INVALID_SIGNATURE: 'error.idv.credential.invalid.signature', + /** + * Reason: Could not anchor the credential, + * possibly caused by errors while connecting to an external provider + * Troubleshooting: Try again later + */ + ERROR_IDV_CREDENTIAL_FAILED_ANCHORING: 'error.idv.credential.failed.anchoring', + /** * Reason: The credential has already been signed. * Troubleshooting: The credential is already signed. You must not sign it again @@ -265,7 +272,6 @@ const ErrorCodes = { * Troubleshooting: Make sure to provide the received token */ ERROR_IDV_TOKEN_MISMATCH: 'error.idv.token.mismatch', - // CW ERRORS /** @@ -417,14 +423,13 @@ const ErrorCodes = { * Troubleshooting: Retry the network call again */ ERROR_RETRYABLE_NETWORK_ERROR: 'error.retryable.network' - }; - /** * Enum for ErrorContextTypes * @readonly * @enum { string } */ + const ErrorContextTypes = { MISSING_PROPERTY: 'missing_property', UCA_STATE: 'uca_state', @@ -437,7 +442,6 @@ const ErrorContextTypes = { CREDENTIAL_ITEM: 'credential_item', UCA_ERROR: 'uca_error' }; - module.exports = { ErrorCodes, ErrorContextTypes diff --git a/dist/es/errors/idvErrors.js b/dist/es/errors/idvErrors.js index 5f3277c9..c5febae0 100644 --- a/dist/es/errors/idvErrors.js +++ b/dist/es/errors/idvErrors.js @@ -1,11 +1,14 @@ const _ = require('lodash'); -const { ErrorCodes, ErrorContextTypes } = require('./definitions'); -// These codes are passed in the 'name' value of the error object when the IDV-toolkit +const { + ErrorCodes, + ErrorContextTypes +} = require('./definitions'); // These codes are passed in the 'name' value of the error object when the IDV-toolkit // throws an error // @deprecated - left here for retrofit, use ErrorConstants instead for future versions -const IDVErrorCodes = _.pickBy(ErrorCodes, (v, k) => k.startsWith('ERROR_IDV')); + +const IDVErrorCodes = _.pickBy(ErrorCodes, (v, k) => k.startsWith('ERROR_IDV')); /* * IDVError parses a HTTP Error response body from the IDV-toolkit * Usage: the IDVError can be instantiated directly from the HTTPResponse body e.g. @@ -17,12 +20,15 @@ const IDVErrorCodes = _.pickBy(ErrorCodes, (v, k) => k.startsWith('ERROR_IDV')); * [{ name: ErrorContextType.MISSING_PROPERTY, value: missingProperty }] * @returns an instance of IDVError * */ + + class IDVError { constructor(errorObj) { this.message = errorObj.message; this.errorCode = errorObj.name; this.errorContext = errorObj.data; } + } module.exports = { diff --git a/dist/es/errors/index.js b/dist/es/errors/index.js index 585a934b..42a6cd51 100644 --- a/dist/es/errors/index.js +++ b/dist/es/errors/index.js @@ -1,8 +1,13 @@ const idvErrors = require('./idvErrors'); -const { ErrorCodes, ErrorContextTypes } = require('./definitions'); + +const { + ErrorCodes, + ErrorContextTypes +} = require('./definitions'); module.exports = { ErrorCodes, ErrorContextTypes, idvErrors // For retrofit, will be deprecated on the future + }; \ No newline at end of file diff --git a/dist/es/index.js b/dist/es/index.js index bcfa7c19..fc8fa2fb 100644 --- a/dist/es/index.js +++ b/dist/es/index.js @@ -1,22 +1,48 @@ -const { Claim } = require('./claim/Claim'); +const { + Claim +} = require('./claim/Claim'); + +const { + UserCollectableAttribute +} = require('./uca/UCA'); + const VC = require('./creds/VerifiableCredential'); -const { initServices, services } = require('./services/index'); + +const { + initServices, + services +} = require('./services/index'); + const isValidGlobalIdentifier = require('./isValidGlobalIdentifier'); + const isClaimRelated = require('./isClaimRelated'); + const errors = require('./errors'); + const constants = require('./constants'); + const claimDefinitions = require('./claim/definitions'); + const credentialDefinitions = require('./creds/definitions'); + const aggregate = require('./AggregationHandler'); +const { + schemaLoader +} = require('./schemas/jsonSchema'); + +const CVCSchemaLoader = require('./schemas/jsonSchema/loaders/cvc'); + +const VCCompat = require('./creds/VerifiableCredentialProxy'); /** * Entry Point for Civic Credential Commons * @returns {CredentialCommons} * @constructor */ + + function CredentialCommons() { this.Claim = Claim; - this.VC = VC; this.init = initServices; this.isValidGlobalIdentifier = isValidGlobalIdentifier; this.isClaimRelated = isClaimRelated; @@ -26,8 +52,13 @@ function CredentialCommons() { this.constants = constants; this.claimDefinitions = claimDefinitions; this.credentialDefinitions = credentialDefinitions; + this.schemaLoader = schemaLoader; + this.CVCSchemaLoader = CVCSchemaLoader; + this.UserCollectableAttribute = UserCollectableAttribute; + this.VC = VC; + this.VCCompat = VCCompat; return this; -} +} // to work with entry points in multi module manage the best way + -// to work with entry points in multi module manage the best way module.exports = new CredentialCommons(); \ No newline at end of file diff --git a/dist/es/isClaimRelated.js b/dist/es/isClaimRelated.js index e131c4d1..e66accb9 100644 --- a/dist/es/isClaimRelated.js +++ b/dist/es/isClaimRelated.js @@ -1,6 +1,15 @@ const _ = require('lodash'); -const { definitions, Claim } = require('./claim/Claim'); + +const { + definitions, + Claim +} = require('./claim/Claim'); + const vcDefinitions = require('./creds/definitions'); + +const { + schemaLoader +} = require('./schemas/jsonSchema'); /** * Validate an claim path against it's parent UserCollectableAttribute, and the parent Claim against the * dependencies of an Credential @@ -9,23 +18,32 @@ const vcDefinitions = require('./creds/definitions'); * @param credential the parent identifier, eg: civ:Credential:GenericId * @return true if the dependency exists and false if it doesn't */ -function isClaimRelated(claim, uca, credential) { - // first get the UCA identifier - const ucaIdentifier = uca.substring(uca.indexOf('-') + 1, uca.lastIndexOf('-')); - // check on the credential commons if this identifier exists - const ucaDefinition = definitions.find(definition => definition.identifier === ucaIdentifier); - // does the UCA exist? + + +async function isClaimRelated(claim, uca, credential) { + // Load the schema and it's references from a source to be used for validation and defining the schema definitions + await schemaLoader.loadSchemaFromTitle(claim); + await schemaLoader.loadSchemaFromTitle(uca); + await schemaLoader.loadSchemaFromTitle(credential); // first get the UCA identifier + + const ucaIdentifier = uca.substring(uca.indexOf('-') + 1, uca.lastIndexOf('-')); // Load the schema and it's references from a source to be used for validation and defining the schema definitions + + await schemaLoader.loadSchemaFromTitle(ucaIdentifier); // check on the credential commons if this identifier exists + + const ucaDefinition = definitions.find(definition => definition.identifier === ucaIdentifier); // does the UCA exist? + if (ucaDefinition) { - const ucaProperties = Claim.getAllProperties(ucaIdentifier); + const ucaProperties = await Claim.getAllProperties(ucaIdentifier); // does the claim exists in the Claim? - // does the claim exists in the Claim? if (_.includes(ucaProperties, claim)) { // we now have the composite uca, the uca for the claim property, they both are correct // we need to check now the UCA is inside the dependencies of the credential refered as parent const credentialDefinition = vcDefinitions.find(definition => definition.identifier === credential); + if (credentialDefinition) { return _.includes(credentialDefinition.depends, ucaIdentifier); } + throw new Error('Credential identifier does not exist'); } else { throw new Error('Claim property path does not exist on UCA definitions'); diff --git a/dist/es/isValidGlobalIdentifier.js b/dist/es/isValidGlobalIdentifier.js index a8b8bad8..c8074444 100644 --- a/dist/es/isValidGlobalIdentifier.js +++ b/dist/es/isValidGlobalIdentifier.js @@ -1,14 +1,18 @@ const _ = require('lodash'); -const { definitions } = require('@identity.com/uca'); -const vcDefinitions = require('./creds/definitions'); -const claimDefinitions = require('./claim/definitions'); -const validUCAIdentifiers = _.map(definitions, d => d.identifier); -const validClaimIdentifiers = _.map(claimDefinitions, d => d.identifier); -const validVCIdentifiers = _.map(vcDefinitions, d => d.identifier); +const { + schemaLoader +} = require('./schemas/jsonSchema'); + +const validUCAIdentifiers = schemaLoader.validIdentifiers; +const validClaimIdentifiers = schemaLoader.validIdentifiers; +const validVCIdentifiers = schemaLoader.validCredentialIdentifiers; const validPrefixes = ['claim', 'credential']; -function isValidGlobalIdentifier(identifier) { +async function isValidGlobalIdentifier(identifier) { + // Load the schema and it's references from a source to be used for validation and defining the schema definitions + await schemaLoader.loadSchemaFromTitle(identifier); + const splited = _.split(identifier, '-'); if (splited.length !== 3) { @@ -24,12 +28,16 @@ function isValidGlobalIdentifier(identifier) { if (!_.includes(validUCAIdentifiers, splited[1]) && !_.includes(validClaimIdentifiers, identifier)) { throw new Error(`${identifier} is not valid`); } + return true; + case 'credential': if (!_.includes(validVCIdentifiers, splited[1]) && !_.includes(validVCIdentifiers, identifier)) { throw new Error(`${identifier} is not valid`); } + return true; + default: return false; } diff --git a/dist/es/lib/did.js b/dist/es/lib/did.js new file mode 100644 index 00000000..86f170ad --- /dev/null +++ b/dist/es/lib/did.js @@ -0,0 +1,72 @@ +const { + findVerificationMethod, + CachedResolver +} = require('@digitalbazaar/did-io'); + +const didSol = require('@identity.com/did-io-driver-sol').default; + +const resolver = new CachedResolver(); // no payer needed as we are only resolving documents + +resolver.use(didSol.driver({ + payer: null +})); +module.exports = { + /** + * Checks if a verificationMethod can sign for the DID document + * + * @param didOrDocument A DID (string) or DID document (object) + * @param verificationMethod The verification method to check + * @returns {Promise} True if the verification method can sign + */ + async canSign(didOrDocument, verificationMethod) { + const [verificationMethodDid] = verificationMethod.split('#'); + const document = didOrDocument.id ? didOrDocument : await this.resolve(didOrDocument); + const did = document.id; // if the verificationMethod DID is for the document DID + + if (verificationMethodDid === did) { + return this.findVerificationMethod(document, verificationMethod) !== null; + } + + if (!document.controller.includes(verificationMethodDid)) { + // If the verification method DID is not a controller of the provided DID + return false; + } // Check if the verificationMethod exists on the controller DID document + + + const controllerDocument = await this.resolve(verificationMethodDid); + return this.findVerificationMethod(controllerDocument, verificationMethod) !== null; + }, + + /** + * Resolves a DID document + * + * @param did The DID to resolve the document for + */ + async resolve(did) { + return resolver.get({ + did + }); + }, + + /** + * Finds the verificationMethod in a document + * + * @param document The document to search through + * @param verificationMethod The verification method to return + */ + findVerificationMethod(document, verificationMethod) { + if (document.keyAgreement && document.keyAgreement.length > 0) { + return document.keyAgreement.find(agreement => agreement.id === verificationMethod); + } + + if (!document.capabilityInvocation.includes(verificationMethod)) { + return null; + } + + return findVerificationMethod({ + doc: document, + methodId: verificationMethod + }); + } + +}; \ No newline at end of file diff --git a/dist/es/lib/signerVerifier.js b/dist/es/lib/signerVerifier.js new file mode 100644 index 00000000..3fd0d842 --- /dev/null +++ b/dist/es/lib/signerVerifier.js @@ -0,0 +1,123 @@ +const nacl = require('tweetnacl'); + +const bs58 = require('bs58'); + +const didUtil = require('./did'); + +class Ed25519Signer { + constructor(key, verificationMethod) { + this.key = key; + this.verificationMethod = verificationMethod; + } + + sign(proof) { + const signed = nacl.sign.detached(Buffer.from(proof.merkleRoot, 'hex'), bs58.decode(this.key)); + const signature = Buffer.from(signed).toString('hex'); + return { + signature, + verificationMethod: this.verificationMethod + }; + } + +} + +class Ed25519Verifier { + constructor(key) { + this.key = key; + } + + verify(vc) { + return nacl.sign.detached.verify(Buffer.from(vc.proof.merkleRoot, 'hex'), Uint8Array.from(Buffer.from(vc.proof.merkleRootSignature.signature, 'hex')), bs58.decode(this.key)); + } + +} +/** + * Creates a signer from the provided information + * + * @param options Signer options: + * @param options.verificationMethod The verificationMethod for the signing key + * @param options.keypair The keypair to sign with + * or + * @param options.privateKey The private key to sign with + * or + * @param options.signer An object implementing a `sign(CvcMerkleProof)` method + */ + + +const signer = async options => { + if (!options.signer && !options.keypair && !options.privateKey) { + throw new Error('Either a signer, keypair or privateKey is required'); + } + + const { + verificationMethod + } = options; + let { + signer: signerImpl + } = options; // Create a signer from keypair/key + + if (signerImpl) return signerImpl; + const [did] = verificationMethod.split('#'); + const document = await didUtil.resolve(did); + let { + privateKey + } = options; + + if (!privateKey) { + privateKey = bs58.encode(options.keypair.secretKey); + } + + const foundMethod = didUtil.findVerificationMethod(document, verificationMethod); + + if (!foundMethod) { + throw new Error('The provided verificationMethod is not valid on the DID document'); + } // Check the type is supported and assign the appropriate signer + + + switch (foundMethod.type) { + case 'Ed25519VerificationKey2018': + case 'Ed25519VerificationKey2020': + signerImpl = new Ed25519Signer(privateKey, verificationMethod); + break; + + default: + throw new Error(`Unsupported type ${foundMethod.type}`); + } + + return signerImpl; +}; +/** + * Creates a verifier based on the information provided + * @param did The issuer DID + * @param verificationMethod The verification method used to lookup the key + */ + + +const verifier = async (did, verificationMethod) => { + const canSignFor = await didUtil.canSign(did, verificationMethod); + + if (!canSignFor) { + // always return false + return { + verify: () => false + }; + } + + const [vmDid] = verificationMethod.split('#'); + const document = await didUtil.resolve(vmDid); + const foundMethod = didUtil.findVerificationMethod(document, verificationMethod); // Check the type is supported and assign the appropriate verifier + + switch (foundMethod.type) { + case 'Ed25519VerificationKey2018': + case 'Ed25519VerificationKey2020': + return new Ed25519Verifier(foundMethod.publicKeyBase58); + + default: + throw new Error(`Unsupported type ${foundMethod.type}`); + } +}; + +module.exports = { + signer, + verifier +}; \ No newline at end of file diff --git a/dist/es/lib/stringUtils.js b/dist/es/lib/stringUtils.js new file mode 100644 index 00000000..6529b4d9 --- /dev/null +++ b/dist/es/lib/stringUtils.js @@ -0,0 +1,17 @@ +/** + * Convert strings like "SocialSecurity" to "socialSecurity". + * If passed a non-PascalCase string such as SOCIAL_SECURITY, it cannot detect that the string + * is not PascalCase and will therefore convert it to sOCIAL_SECURITY + * @param string + * @return {*} + */ +const pascalToCamelCase = string => string.replace(/^([A-Z])/, match => match.toLowerCase()); + +const identifierPattern = /(claim|credential|uca|type)-((\w+):[\w.:]+)-v(\d+)/; + +const parseIdentifier = identifier => identifier.match(identifierPattern); + +module.exports = { + pascalToCamelCase, + parseIdentifier +}; \ No newline at end of file diff --git a/dist/es/logger.js b/dist/es/logger.js index 5569f055..e77d6e08 100644 --- a/dist/es/logger.js +++ b/dist/es/logger.js @@ -1,11 +1,25 @@ -const { createLogger, format, transports } = require('winston'); +module.exports = { + // eslint-disable-next-line no-unused-vars + warn(a, b = undefined) { + // eslint-disable-next-line no-console + console.warn(a); + }, -// Configure the Winston logger. For the complete documentation see https://github.com/winstonjs/winston -const logger = createLogger({ - // To see more detailed errors, change this to 'debug' - level: 'info', - format: format.combine(format.splat(), format.simple()), - transports: [new transports.Console()] -}); + // eslint-disable-next-line no-unused-vars + info(a, b = undefined) { + // eslint-disable-next-line no-console + console.info(a); + }, -module.exports = logger; \ No newline at end of file + // eslint-disable-next-line no-unused-vars + debug(a, b = undefined) {// eslint-disable-next-line no-console + // console.debug(a); // disabled + }, + + // eslint-disable-next-line no-unused-vars + error(a, b = undefined) { + // eslint-disable-next-line no-console + console.error(a); + } + +}; \ No newline at end of file diff --git a/dist/es/schemas/generator/SchemaGenerator.js b/dist/es/schemas/generator/SchemaGenerator.js deleted file mode 100644 index f6fc731f..00000000 --- a/dist/es/schemas/generator/SchemaGenerator.js +++ /dev/null @@ -1,255 +0,0 @@ -/* eslint-disable no-use-before-define */ -const _ = require('lodash'); -const randomString = require('randomstring'); -const Type = require('type-of-is'); -const RandExp = require('randexp'); -const { UserCollectableAttribute: UCA, definitions: ucaDefinitions } = require('@identity.com/uca'); -const { Claim, definitions, getBaseIdentifiers } = require('../../claim/Claim'); - -const DRAFT = 'http://json-schema.org/draft-07/schema#'; - -const getPropertyNameFromDefinition = definition => getBaseIdentifiers(definition.identifier).identifierComponents[2]; - -const getPropertyType = value => Type.string(value).toLowerCase(); - -const processObject = (object, outputParam, parentKey) => { - const output = outputParam || {}; - const objectProperties = object; - const { definition } = objectProperties; - delete objectProperties.definition; - - output.type = getPropertyType(objectProperties); - output.properties = output.properties || {}; - const keys = Object.entries(objectProperties); - - // too much debate on this eslint - // https://github.com/airbnb/javascript/issues/1122 - // eslint-disable-next-line no-restricted-syntax - for (const [key, value] of keys) { - // we have to get the required array from the definitions properties - - const type = getPropertyType(value); - if (type === 'object') { - output.properties[key] = processObject(value, output.properties[key], `${parentKey}.${key}`); - } else if (type === 'array') { - // recursion - // eslint-disable-next-line - output.properties[key] = processArray(value, output.properties[key], key); - } else { - output.properties[key] = {}; - output.properties[key].type = type === 'null' ? ['null', 'string'] : type; - if (definition && definition.type.properties) { - let propType = definition.type.properties.find(prop => prop.name === key); - // simple composite, one depth level civ:Identity.name for example - if (propType && propType.type.includes(':')) { - propType = definitions.find(def => def.identifier === propType.type); - } - - output.properties[key] = addMinimumMaximum(propType, output.properties[key]); - } else { - output.properties[key] = addMinimumMaximum(definition, output.properties[key]); - } - } - } - // it must be 4 here, we start the json of the VC with root - // then it's claim, then all standardize Claim are type:name - if (parentKey.includes('claim') && parentKey.split('.').length === 4) { - // with the json key of the claim - const baseUcaName = parentKey.substring('root.claim.'.length); - let typeName = (baseUcaName.substring(0, 1).toUpperCase() + baseUcaName.substring(1)).replace('.', ':'); - // regenerate uca - let refDefinition = definitions.find(def => def.identifier.includes(typeName)); - if (refDefinition == null) { - const baseName = baseUcaName.substring(0, 1).toUpperCase() + baseUcaName.substring(1); - typeName = `claim-cvc:${baseName}-v1`; - refDefinition = definitions.find(def => def.identifier.includes(typeName)); - } - if (refDefinition == null) { - typeName = `claim-cvc:${baseUcaName}-v1`; - refDefinition = definitions.find(def => def.identifier.includes(typeName)); - } - // get it's required definitions - output.required = refDefinition.type.required; - } - output.additionalProperties = false; - return output; -}; - -const processArray = (array, outputParam) => { - const output = outputParam || {}; - output.type = getPropertyType(array); - output.items = output.items || {}; - const type = getPropertyType(array[0]); - output.items.type = type; - output.additionalProperties = false; - return output; -}; - -/** - * Entry point of this class. Use this to generate an sample json data - * then an json schema from that data. That way you do not need to - * create sample or mocks json from Credentials - * - * @param definition Claim/VC definition - * @param json generated json - * @returns {{$schema: string}} expected json schema to validate this data - */ -const process = (definition, json) => { - const object = json; - const title = definition.identifier; - let processOutput; - const output = { - $schema: DRAFT - }; - output.title = title; - // Set initial object type - output.type = Type.string(object).toLowerCase(); - - // Process object - if (output.type === 'object') { - processOutput = processObject(object, {}, 'root'); - output.type = processOutput.type; - output.properties = processOutput.properties; - } - - // never allow additionalProperties - output.additionalProperties = false; - // Output - return output; -}; - -/** - * Build a sample json from an definition identifier - * Recursively make the Claim from nested properties and Claim references - * - * @param definition receive an Claim and build an sample json from it's properties - * @returns {{$schema: string}} - */ -const buildSampleJson = (definition, includeDefinitions = false) => { - let output = {}; - output = makeJsonRecursion(definition, includeDefinitions); - return output; -}; - -/** - * Recursion to build the schema from an json value - * @param ucaDefinition - */ -const makeJsonRecursion = (ucaDefinition, includeDefinitions = false) => { - let output = {}; - const typeName = Claim.getTypeName(ucaDefinition); - if (typeof ucaDefinition.type === 'object' && ucaDefinition.type.properties !== undefined) { - // array of properties - ucaDefinition.type.properties.forEach(property => { - output[property.name] = generateRandomValueForType(property, includeDefinitions); - }); - } else if (typeName === 'Array') { - const itemType = ucaDefinition.items.type; - const itemDefinition = _.find(definitions, { identifier: itemType }); - output = [makeJsonRecursion(itemDefinition, includeDefinitions)]; - } else if (typeName !== 'Object') { - // not a reference - const propertyName = getPropertyNameFromDefinition(ucaDefinition); - if (typeof ucaDefinition.pattern !== 'undefined' && ucaDefinition.pattern !== null) { - output[propertyName] = new RandExp(ucaDefinition.pattern).gen(); - } else { - output[propertyName] = generateRandomValueForType(ucaDefinition, includeDefinitions); - } - } else { - // a direct reference to a composite type - output = generateRandomValueForType(ucaDefinition, includeDefinitions); - } - if (includeDefinitions && output.definition == null) { - output.definition = ucaDefinition; - } - return output; -}; - -/** - * This method is an auxiliary method to allow random values to easy create - * json schemas from JSON values generated from Claim/VC - * - * @param definition - * @returns {number} - */ -const generateRandomNumberValueWithRange = definition => { - let genRandomNumber = Math.random() * 100; - - if (definition !== null) { - if (typeof definition.minimum !== 'undefined' && definition.minimum !== null && genRandomNumber < definition.minimum) { - genRandomNumber = definition.minimum; - } - - if (definition.exclusiveMinimum !== 'undefined' && definition.exclusiveMinimum !== null && genRandomNumber <= definition.exclusiveMinimum) { - genRandomNumber = definition.exclusiveMinimum + 0.1; - } - - if (typeof definition.maximum !== 'undefined' && definition.maximum !== null && genRandomNumber > definition.maximum) { - genRandomNumber = definition.maximum; - } - - if (definition.exclusiveMaximum !== 'undefined' && definition.exclusiveMaximum !== null && genRandomNumber >= definition.exclusiveMaximum) { - genRandomNumber = definition.exclusiveMaximum - 0.1; - } - } - - return genRandomNumber; -}; - -const generateRandomValueForType = (definition, includeDefinitions = false) => { - const typeName = definition.type; - let refDefinition = definition; - let resolvedTypeName = typeName; - if (typeName.includes(':')) { - // simple composite, one depth level civ:Identity.name for example - refDefinition = definitions.find(def => def.identifier === typeName); - if (refDefinition != null) { - resolvedTypeName = Claim.resolveType(refDefinition); - } else { - refDefinition = ucaDefinitions.find(def => def.identifier === typeName); - if (refDefinition) { - resolvedTypeName = UCA.resolveType(refDefinition); - } - } - } - // generate sample data - // that's why the magic numbers are here - switch (resolvedTypeName) { - case 'String': - if (refDefinition.enum) { - return _.values(refDefinition.enum)[0]; - } - return refDefinition && refDefinition.pattern ? new RandExp(refDefinition.pattern).gen() : randomString.generate(10); - case 'Number': - return generateRandomNumberValueWithRange(refDefinition); - case 'Boolean': - return Math.round(Math.random()) === 1; - default: - return makeJsonRecursion(refDefinition, includeDefinitions); - } -}; - -const addMinimumMaximum = (definition, object) => { - const output = object; - // for simple Claim get json schema properties - if (typeof definition !== 'undefined' && definition !== null) { - if (definition.exclusiveMinimum != null) { - output.exclusiveMinimum = definition.exclusiveMinimum; - } - - if (definition.minimum != null) { - output.minimum = definition.minimum; - } - - if (definition.exclusiveMaximum != null) { - output.exclusiveMaximum = definition.exclusiveMaximum; - } - - if (definition.maximum != null) { - output.maximum = definition.maximum; - } - } - return output; -}; - -module.exports = { process, buildSampleJson }; \ No newline at end of file diff --git a/dist/es/schemas/jsonSchema/index.js b/dist/es/schemas/jsonSchema/index.js new file mode 100644 index 00000000..60047e62 --- /dev/null +++ b/dist/es/schemas/jsonSchema/index.js @@ -0,0 +1,560 @@ +const _ = require('lodash'); + +const Ajv = require('ajv').default; + +const traverse = require('json-schema-traverse'); + +const { + definitions: ucaDefinitions +} = require('@identity.com/uca'); + +const addFormats = require('ajv-formats').default; + +const definitions = require('../../claim/definitions'); + +const credentialDefinitions = require('../../creds/definitions'); + +let summaryMap = {}; +/** + * Code generated to create a sumary map for a human readable output. + */ + +class SummaryMapper { + static addDefinition(def) { + const textLabel = SummaryMapper.getTextLabel(def.identifier); + + if (textLabel) { + const mapItem = _.get(summaryMap, textLabel); + + if (mapItem) { + mapItem.labelFor.push(def.identifier); + } else { + summaryMap[textLabel] = { + identifier: def.identifier, + textLabel, + credentials: SummaryMapper.getCredentials(def.identifier), + labelFor: [def.identifier], + changeable: SummaryMapper.isUpdatable(textLabel), + claimPath: SummaryMapper.getClaimPath(def.identifier) + }; + } + } + } + + static addCredentialDefinition(def) { + const textLabel = SummaryMapper.getTextLabel(def.identifier); + + if (textLabel) { + summaryMap[textLabel] = { + identifier: def.identifier, + textLabel, + credentials: [def.identifier], + labelFor: [def.identifier], + changeable: SummaryMapper.isUpdatable(textLabel), + claimPath: null + }; + } + } + + static getTextLabel(identifier) { + // eslint-disable-next-line no-unused-vars + const [type, name, version] = _.split(identifier, '-'); // eslint-disable-next-line no-unused-vars + + + const [namespace, unique, path] = _.split(name, ':'); + + if (type && unique) { + return `${unique}.${type}${path ? `.${path}` : ''}`.toLowerCase(); + } + + return null; + } + + static isUpdatable(textLabel) { + const notUpdatable = ['document.placeofbirth.claim', 'document.dateofbirth.claim']; + return !_.includes(notUpdatable, textLabel); + } + + static getCredentials(identifier) { + // eslint-disable-next-line no-unused-vars + const [type, name, version] = _.split(identifier, '-'); + + if (type === 'credential') { + return [identifier]; + } + + const credentials = _.filter(credentialDefinitions, item => _.includes(item.depends, identifier)); + + return _.map(credentials, item => item.identifier); + } + + static getClaimPath(identifier) { + // eslint-disable-next-line no-unused-vars + const [type, name, version] = _.split(identifier, '-'); + + if (type === 'credential') { + return null; + } + + return SummaryMapper.getPath(identifier); + } + + static getBaseIdentifiers(identifier) { + const claimRegex = /claim-cvc:(.*)\.(.*)-v\d*/; + let isNewIdentifier = true; + let identifierComponents = claimRegex.exec(identifier); + + if (identifierComponents === null) { + identifierComponents = _.split(identifier, ':'); + isNewIdentifier = false; + } + + return { + identifierComponents, + isNewIdentifier + }; + } + + static getPath(identifier) { + const { + identifierComponents + } = SummaryMapper.getBaseIdentifiers(identifier); + + const baseName = _.camelCase(identifierComponents[1]); + + return baseName !== 'type' ? `${baseName}.${identifierComponents[2]}` : identifierComponents[2]; + } + +} + +const getSchemaVersion = identifier => { + const matches = identifier.match(/-v([\d]+$)/); + + if (matches && matches.length > 1) { + return matches[1]; + } + + return '1'; +}; + +function transformUcaIdToClaimId(identifier) { + const identifierComponents = identifier.split(':'); + return `claim-cvc:${identifierComponents[1]}.${identifierComponents[2]}-v1`; +} + +function isDefinitionEqual(definition, ucaDefinition) { + return definition.identifier === transformUcaIdToClaimId(ucaDefinition) || definition.identifier === ucaDefinition; +} + +const isUCA = uca => /^[^:]+:[^:]+:[^:]+$/.test(uca); +/** + * This class loads the schema definitions as needed by using loaders provided by the + */ + + +class SchemaLoader { + constructor() { + this.loaders = []; + this.definitions = definitions; + this.ucaDefinitions = ucaDefinitions; + this.credentialDefinitions = credentialDefinitions; + this.summaryMap = summaryMap; + this.validIdentifiers = []; + this.validUcaIdentifiers = []; + this.validCredentialIdentifiers = []; + this.ucaCompared = []; // allowUnionTypes is required because of the anchor in the proof can be a string/object (this should be changed) + + this.ajv = new Ajv({ + logger: console, + allErrors: true, + verbose: true, + strict: true, + allowUnionTypes: true + }); // add data formats such as date-time + + addFormats(this.ajv); + this.ajv.addKeyword('attestable'); // Needed to add these to support "reversing" definitions back to the previous definitions for backwards + // compatibilty. These should be removed? + + this.ajv.addKeyword('transient'); + this.ajv.addKeyword('credentialItem'); + this.ajv.addKeyword('alias'); + this.ajv.addKeyword('deambiguify'); + } + + reset() { + this.ucaDefinitions.length = 0; + this.definitions.length = 0; + this.credentialDefinitions.length = 0; + this.validIdentifiers.length = 0; + this.validCredentialIdentifiers.length = 0; + this.validUcaIdentifiers.length = 0; + this.ajv.removeSchema(/.*/); + summaryMap = {}; + this.summaryMap = summaryMap; + } + /** + * Adds a schema loader which references where the schemas are loaded from + */ + + + addLoader(loader) { + this.loaders.push(loader); + } + + async loadSchemaFromUri(uri) { + const title = uri.split('#')[0].match('[^/]+$', uri); + const schema = await this.loadSchemaFromTitle(title[0]); + return schema; + } + + async loadPropertySchema(schema, definition, ref, property) { + const propertySchema = await this.loadSchemaFromUri(ref); + + if (propertySchema !== null) { + definition.depends.push(propertySchema.title); + } + + const csProperties = await this.getCredentialSubjectProperties(schema); + + if (csProperties.required && csProperties.required.includes(property)) { + definition.required.push(propertySchema.title); + } + } + /** + * Supporting both claim and credentialSubject + * TODO: remove this once backwards compatibility has been removed + * @param schema + * @returns {*|(() => Promise)} + */ + + + async getCredentialSubjectProperties(schema) { + const schemaProperties = await this.flattenCredentialSchemaProperties(schema); + return schemaProperties.credentialSubject ? schemaProperties.credentialSubject : schemaProperties.claim; + } + /** + * Flattens the properties of a schema if there are any referenced schemas + * @param schema + * @returns {Promise<*>} + */ + + + async flattenCredentialSchemaProperties(schema) { + let properties = schema.properties ? schema.properties : {}; + + if (schema.allOf) { + const promises = schema.allOf.map(async allOf => { + if (allOf.$ref) { + const refSchema = await this.loadSchemaFromUri(allOf.$ref); + const refProperties = await this.flattenCredentialSchemaProperties(refSchema); + properties = { ...properties, + ...refProperties + }; + } + + if (allOf.properties) { + properties = { ...properties, + ...allOf.properties + }; + } + }); + await Promise.all(promises); + } + + return properties; + } + /** + * Adds a claim definition to be backwards compatible with the old schema structure. + */ + + + async addDefinition(schema) { + if (/^credential-/.test(schema.title)) { + await this.addCredentialDefinition(schema); + } else { + await this.addClaimDefinition(schema); + } + } + /** + * Adds a credential definition to be backwards compatible with the old schema structure. + */ + + + async addCredentialDefinition(schema) { + const definition = { + identifier: schema.title, + version: getSchemaVersion(schema.title), + depends: [] + }; + + if (schema.transient) { + definition.transient = true; + } + + const credentialSubjectDefinition = await this.getCredentialSubjectProperties(schema); + + if (credentialSubjectDefinition.required) { + definition.required = []; + } + + const references = []; + + _.forEach(credentialSubjectDefinition.properties, vo => { + _.forEach(vo.properties, (vi, ki) => { + references.push({ + ref: vo.properties[ki].$ref, + property: ki + }); + }); + }); + + await _.reduce(references, async (promise, value) => { + await promise; + return this.loadPropertySchema(schema, definition, value.ref, value.property); + }, Promise.resolve()); + this.credentialDefinitions.push(definition); + this.validCredentialIdentifiers.push(definition.identifier); + SummaryMapper.addCredentialDefinition(definition); + } + + async shouldAddClaimDefinition(schema) { + if (isUCA(schema.title)) { + const transformed = transformUcaIdToClaimId(schema.title); + + if (!this.ucaCompared.includes(schema.title)) { + await this.loadSchemaFromTitle(transformed); + } + + this.ucaCompared.push(schema.title); + let found = false; + this.definitions.some(definition => { + if (isDefinitionEqual(definition, schema.title)) { + found = true; + } + + return found; + }); + + if (found) { + return false; + } + } + + return true; + } + + async addClaimDefinition(schema) { + const definition = { + identifier: schema.title, + version: getSchemaVersion(schema.title), + type: await this.findDefinitionType(schema) + }; + + if (definition.type === 'Array') { + const subSchema = await this.loadSchemaFromUri(schema.items.$ref); + definition.items = { + type: subSchema.title + }; + } + + ['attestable', 'credentialItem', 'minimum', 'maximum', 'alias', 'description'].forEach(property => { + if (property in schema) { + definition[property] = schema[property]; + } + }); + + if (schema.pattern) { + // definition.pattern = new RegExp(schema.pattern.substring(1, schema.pattern.length - 1)); + definition.pattern = new RegExp(schema.pattern); + } + + if (schema.required) { + definition.type.required = schema.required; + } + + if (schema.enum) { + definition.enum = {}; + + _.forEach(schema.enum, value => { + definition.enum[value.toUpperCase()] = value; + }); + } + + if (await this.shouldAddClaimDefinition(schema)) { + this.definitions.push(definition); + this.validIdentifiers.push(schema.title); + } + + if (isUCA(schema.title)) { + this.ucaDefinitions.push(definition); + this.validUcaIdentifiers.push(schema.title); + } + + SummaryMapper.addDefinition(definition); + } + + async getPropertyValue(defProperties, property, name) { + const { + deambiguify, + items + } = property; + let { + type + } = property; + + if (type === 'array' || items && items.$ref) { + if (items.$ref) { + const arraySchema = await this.loadSchemaFromUri(items.$ref); + type = arraySchema.title; + } else { + type = _.capitalize(type); + } + } + + if (property.allOf) { + const schema = await this.loadSchemaFromUri(property.allOf[0].$ref); + type = schema.title; + } + + const defProperty = { + name, + type + }; + + if (deambiguify) { + defProperty.deambiguify = deambiguify; + } + + defProperties.push(defProperty); + } + + async getPropertyValues(properties) { + const defProperties = []; + await _.reduce(properties, async (promise, value, name) => { + await promise; + return this.getPropertyValue(defProperties, value, name); + }, Promise.resolve()); + return { + properties: defProperties + }; + } + /** + * Finds the definition/properties of a schema + */ + + + async findDefinitionType(schema) { + if (schema.allOf) { + const subSchema = await this.loadSchemaFromUri(schema.allOf[0].$ref); + + if (subSchema == null) { + return null; + } + + return subSchema.title; + } + + if (schema.type === 'object') { + if (!_.isEmpty(schema.properties)) { + return this.getPropertyValues(schema.properties); + } + } + + if (schema.type === 'array') { + return 'Array'; + } + + return _.capitalize(schema.type); + } + /** + * Loads a schema, traversing all the subschemas and loading them as well + */ + + + async loadSchemaFromTitle(title) { + const loader = this.findSchemaLoader(title); + + if (loader == null) { + return null; + } + + const schemaId = loader.schemaId(title); + const existingSchema = this.ajv.getSchema(schemaId); + let schema; // If AJV is unaware of the schema, look it up and create it + + if (!existingSchema) { + schema = await loader.loadSchema(title); + + if (schema === null) { + return null; + } // Loads all referenced schemas + + + const references = []; + traverse(schema, { + cb: currentNode => { + if (currentNode.$ref !== undefined && !currentNode.$ref.startsWith('#')) { + // Prevent the same schema loaded multiple times + references.push(this.loadSchemaFromUri(currentNode.$ref)); + } + } + }); + await Promise.all(references); + await this.addDefinition(schema); + + try { + this.ajv.addSchema(schema); + } catch (e) {// This could only happen if we have a cyclic dependency, or the same ref multiple times in the schema... + } + + return schema; + } + + return existingSchema.schema; + } + /** + * Finds the correct schema loader based on the identifier + */ + + + findSchemaLoader(identifier) { + return _.find(this.loaders, loader => loader.valid(identifier)); + } + /** + * Validates the schema based on identifier and supplied data. + */ + + + async validateSchema(identifier, data) { + const loader = this.findSchemaLoader(identifier); + await this.loadSchemaFromTitle(identifier); + this.validate(loader.schemaId(identifier), data); + } + + validate(schemaRef, value) { + const validateSchema = this.ajv.getSchema(schemaRef); + + if (typeof validateSchema === 'undefined') { + throw new Error(`Invalid schema id: ${schemaRef}`); + } + + const valid = validateSchema(value); + + if (!valid) { + _.forEach(validateSchema.errors, error => { + if (error.params && error.params.missingProperty) { + throw new Error(`Missing required fields to ${validateSchema.schema.title}`); + } + }); + + throw new Error(`Invalid value. Errors: ${JSON.stringify(validateSchema.errors, null, 2)}`); + } + } + +} + +const schemaLoader = new SchemaLoader(); +module.exports = { + schemaLoader +}; \ No newline at end of file diff --git a/dist/es/schemas/jsonSchema/loaders/cvc.js b/dist/es/schemas/jsonSchema/loaders/cvc.js new file mode 100644 index 00000000..19644f35 --- /dev/null +++ b/dist/es/schemas/jsonSchema/loaders/cvc.js @@ -0,0 +1,147 @@ +const fs = require('fs'); + +const { + parseIdentifier +} = require('../../../lib/stringUtils'); + +const { + services +} = require('../../../services'); + +const rootUri = 'http://identity.com/schemas/'; +const DEFAULT_SCHEMA_PATH = 'http://dev-schemas.civic.com.s3-website-us-east-1.amazonaws.com/dev'; + +class FSSchemaCache { + constructor(cachePath = './.tmp/schemas') { + this.cachePath = cachePath; + fs.mkdirSync(cachePath, { + recursive: true + }); + } + + get(identifier) { + const cachePath = `${this.cachePath}/${identifier}.schema.json`; + + if (!fs.existsSync(cachePath)) { + return null; + } + + return fs.readFileSync(cachePath, { + encoding: 'utf8' + }); + } + + set(identifier, schema) { + const cachePath = `${this.cachePath}/${identifier}.schema.json`; + fs.writeFileSync(cachePath, schema, { + encoding: 'utf8' + }); + } + +} + +const getIdentifierPath = identifier => { + let identifierPath; + + if (/^cvc:.*$/.test(identifier)) { + identifierPath = `uca/1/${identifier}`; + } else { + const parsedIdentifier = parseIdentifier(identifier); + + if (parsedIdentifier) { + identifierPath = `${parsedIdentifier[1]}/${parsedIdentifier[4]}/${parsedIdentifier[2]}`; + } + } + + return identifierPath; +}; +/** + * This is a sample schema loader, to be used for testing or civic.com claims & credential implementations + */ + + +class CVCLoader { + constructor(http = services.container.Http, cache = new FSSchemaCache(), schemaPath = DEFAULT_SCHEMA_PATH) { + this.http = http; + this.cache = cache; + this.schemaPath = schemaPath; + } + /** + * Gets the schema id based on the identifier + */ + // eslint-disable-next-line class-methods-use-this + + + schemaId(identifier) { + return rootUri + identifier; + } + /** + * Tests to see if this loader is valid for the supplied identifier + */ + // eslint-disable-next-line class-methods-use-this + + + valid(identifier) { + return /^(claim|credential|type)-(cvc|alt):.*$/.test(identifier) || /^cvc:.*$/.test(identifier); + } + /** + * Loads the schema based on the identifier + */ + + + async loadSchema(identifier) { + let schema = null; + + if (this.cache) { + schema = this.cache.get(identifier); + } // Only load the schema remotely if a base url was provided and none was found locally + + + if (!schema) { + schema = await this.remote(identifier); + + if (this.cache && schema) { + this.cache.set(identifier, schema); + } + } + + try { + return !schema ? null : JSON.parse(schema); + } catch (e) { + return null; + } + } + /** + * Loads a schema from a remote location + * @param identifier The identifer to load the schema for + * @returns The schema object if found + */ + + + async remote(identifier) { + const identifierPath = getIdentifierPath(identifier); + + if (!identifierPath) { + return null; + } + + const uri = `${this.schemaPath}/${identifierPath}.schema.json`; + let response = null; + + try { + response = await this.http.request(uri); + } catch (e) { + // If it fails due to timeout/connectivity, or a server side issue - try again + if (!e.statusCode || e.statusCode >= 500 && e.statusCode <= 599) { + response = await services.container.Http.request(uri); + } else if (e.statusCode < 400 || e.statusCode >= 500) { + throw e; + } + } + + return response; + } + +} + +module.exports = CVCLoader; \ No newline at end of file diff --git a/dist/es/services/DefaultAnchorServiceImpl.js b/dist/es/services/DefaultAnchorServiceImpl.js index 9a57575d..8706daf0 100644 --- a/dist/es/services/DefaultAnchorServiceImpl.js +++ b/dist/es/services/DefaultAnchorServiceImpl.js @@ -3,19 +3,27 @@ * */ const uuid = require('uuid/v4'); -const { HDNode, ECSignature } = require('bitcoinjs-lib'); + +const { + HDNode, + ECSignature +} = require('bitcoinjs-lib'); + const sjcl = require('sjcl'); -const logger = require('../logger'); +const logger = require('../logger'); /** * An Anchor/Attester implementation * * @param {*} config * @param {*} http */ + + function DummyAnchorServiceImpl(config, http) { this.config = config; - this.http = http; + this.http = http; // eslint-disable-next-line no-unused-vars + const pollService = async statusUrl => { try { const attestation = await this.http.request({ @@ -29,10 +37,12 @@ function DummyAnchorServiceImpl(config, http) { // eslint-disable-next-line no-unused-vars return await pollService(statusUrl); } + if (attestation && attestation.type !== 'permanent') { attestation.statusUrl = statusUrl; return attestation; } + return attestation; } catch (error) { logger.error(`Error polling: ${statusUrl}`, JSON.stringify(error, null, 2)); @@ -67,50 +77,63 @@ function DummyAnchorServiceImpl(config, http) { this.update = async tempAnchor => { tempAnchor.type = 'permanent'; // eslint-disable-line + tempAnchor.value = new uuid(); // eslint-disable-line + return Promise.resolve(tempAnchor); }; this.verifySignature = (proof, pinnedPubKey) => { - const { subject } = proof.anchor; + const { + subject + } = proof.anchor; const anchorSubjectValidation = this.verifySubjectSignature(subject, pinnedPubKey); return anchorSubjectValidation && subject.data === proof.merkleRoot; }; - /** * This method checks if the subject signature matches the pub key * @param subject a json with label, data, signature, pub * @returns {*} true or false for the validation */ + + this.verifySubjectSignature = (subject, pinnedPubKey) => { // Do not use JS destruct on the next line, We need to predict the JSON order - const toHash = JSON.stringify({ xpub: subject.pub, label: subject.label, data: subject.data }); + const toHash = JSON.stringify({ + xpub: subject.pub, + label: subject.label, + data: subject.data + }); const hash = sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash(toHash)); const subjectSignature = ECSignature.fromDER(Buffer.from(subject.signature, 'hex')); return HDNode.fromBase58(pinnedPubKey || subject.pub).keyPair.verify(Buffer.from(hash, 'hex'), subjectSignature); }; - /** * This method checks that the attestation / anchor exists on the BC */ + + this.verifyAttestation = async (proof, offline = true) => { const path = '/proof'; const endpoint = `${this.config.attestationService}${path}`; - const requestOptions = { url: endpoint, - body: { attestation: proof.anchor, offline }, + body: { + attestation: proof.anchor, + offline + }, method: 'POST', json: true, simple: true // reject if not 2XX - }; + }; const response = await this.http.request(requestOptions); return response.valid; }; this.revokeAttestation = async signature => { signature.revoked = true; // eslint-disable-line + return Promise.resolve(signature); }; diff --git a/dist/es/services/DummyAnchorServiceImpl.js b/dist/es/services/DummyAnchorServiceImpl.js index 580e4e1b..e8e347e9 100644 --- a/dist/es/services/DummyAnchorServiceImpl.js +++ b/dist/es/services/DummyAnchorServiceImpl.js @@ -3,17 +3,20 @@ * */ const uuid = require('uuid/v4'); -const logger = require('../logger'); +const logger = require('../logger'); /** * An Anchor/Attester implementation * * @param {*} config * @param {*} http */ + + function DummyAnchorServiceImpl(config, http) { this.config = config; - this.http = http; + this.http = http; // eslint-disable-next-line no-unused-vars + const pollService = async statusUrl => { try { const attestation = await this.http.request({ @@ -27,10 +30,12 @@ function DummyAnchorServiceImpl(config, http) { // eslint-disable-next-line no-unused-vars return await pollService(statusUrl); } + if (attestation && attestation.type !== 'permanent') { attestation.statusUrl = statusUrl; return attestation; } + return attestation; } catch (error) { logger.error(`Error polling: ${statusUrl}`, JSON.stringify(error, null, 2)); @@ -56,7 +61,8 @@ function DummyAnchorServiceImpl(config, http) { path: '/' }, coin: 'dummycoin', - tx: new uuid(), // eslint-disable-line + tx: new uuid(), + // eslint-disable-line network: 'dummynet', type: 'temporary', civicAsPrimary: false, @@ -65,23 +71,25 @@ function DummyAnchorServiceImpl(config, http) { this.update = async tempAnchor => { // eslint-disable-next-line no-param-reassign - tempAnchor.type = 'permanent'; - // eslint-disable-next-line no-param-reassign + tempAnchor.type = 'permanent'; // eslint-disable-next-line no-param-reassign + tempAnchor.value = uuid(); return Promise.resolve(tempAnchor); }; this.verifySignature = () => true; - /** * This method checks if the subject signature matches the pub key * @returns {*} true or false for the validation */ - this.verifySubjectSignature = () => true; + + this.verifySubjectSignature = () => true; /** * This method checks that the attestation / anchor exists on the BC */ + + this.verifyAttestation = async () => true; this.revokeAttestation = async signature => { diff --git a/dist/es/services/MiniCryptoManagerImpl.js b/dist/es/services/MiniCryptoManagerImpl.js index d1f949a0..404f15af 100644 --- a/dist/es/services/MiniCryptoManagerImpl.js +++ b/dist/es/services/MiniCryptoManagerImpl.js @@ -1,5 +1,7 @@ -const { HDNode, ECSignature } = require('bitcoinjs-lib'); - +const { + HDNode, + ECSignature +} = require('bitcoinjs-lib'); /** * MiniCryptoManagerImpl - A minimal CryptoManagerImpl for the portable CryptoManagerInterface * to provide only default sign() and verify() functions to credential-commons with minimal dependencies. @@ -14,16 +16,19 @@ const { HDNode, ECSignature } = require('bitcoinjs-lib'); * You should `installKey` a PVT key or a PUB key (verify only) before call `sign()` or `verify()`. * The installed key is removed after `sign()` or `verify()` function was called. */ + + class MiniCryptoManagerImpl { constructor() { this.KEY_STORAGE = {}; } - /** * Install a pvt or a pub key on a keyName to be used on `sign()` or `verify()` function later. * @param {} keyName - name of the key to be installed. * @param {} key - a pvt or a pub key in base58 format. */ + + installKey(keyName, key) { try { // Test if key is a valid HDNode key @@ -33,7 +38,6 @@ class MiniCryptoManagerImpl { throw new Error(`Invalid key format: ${err.message}`); } } - /** * Return input data signed using the specified key. * @@ -42,20 +46,18 @@ class MiniCryptoManagerImpl { * @param { string } keyName - name of the key to be used to sign. * @param { string } hexHash - hex string representation of the hash */ + + sign(keyName, hexHash) { const privateKey = this.KEY_STORAGE[keyName]; const keyPair = HDNode.fromBase58(privateKey); - const hash = Buffer.from(hexHash, 'hex'); const signature = keyPair.sign(hash); - const hexSignature = signature.toDER().toString('hex'); + const hexSignature = signature.toDER().toString('hex'); // keys are volatile in this impl, removes - // keys are volatile in this impl, removes delete this.KEY_STORAGE[keyName]; - return hexSignature; } - /** * Return true if signature has been verified, false otherwise. * @@ -63,19 +65,19 @@ class MiniCryptoManagerImpl { * @param { string } hexHash - hex string representation of the hash * @param { string } hexSignature - DER encoded signature. */ + + verify(keyName, hexHash, hexSignature) { const key = this.KEY_STORAGE[keyName]; const keyPair = HDNode.fromBase58(key); - const hash = Buffer.from(hexHash, 'hex'); const signature = Buffer.from(hexSignature, 'hex'); - const ecSignature = ECSignature.fromDER(signature); + const ecSignature = ECSignature.fromDER(signature); // keys are volatile in this impl, removes - // keys are volatile in this impl, removes delete this.KEY_STORAGE[keyName]; - return keyPair.verify(hash, ecSignature); } + } module.exports = MiniCryptoManagerImpl; \ No newline at end of file diff --git a/dist/es/services/__mocks__/httpService.js b/dist/es/services/__mocks__/httpService.js index c47062e0..e764b88e 100644 --- a/dist/es/services/__mocks__/httpService.js +++ b/dist/es/services/__mocks__/httpService.js @@ -1,14 +1,22 @@ /* eslint-disable max-len */ - const _ = require('lodash'); + const logger = require('../../logger'); function HttpServiceConstructor() { this.name = 'mockHttp'; + this.request = async (uri, options) => { - logger.debug(`Mocking request for: ${JSON.stringify({ uri, options }, null, 2)}`); - const params = _.isString(uri) ? { url: uri } : uri; + logger.debug(`Mocking request for: ${JSON.stringify({ + uri, + options + }, null, 2)}`); + const params = _.isString(uri) ? { + url: uri + } : uri; + _.assign(params, options); + const responses = [{ path: '/registry', response: { @@ -19,13 +27,19 @@ function HttpServiceConstructor() { } }, { path: '/jwt', - response: { jwt: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJqdGkiOiIyYzdlNjQ4YS1hNDhmLTQxNTgtOGZmMS02MTY0YzM5OWNlNDMiLCJpYXQiOjE1Mjg4MjE1ODUuNDM0LCJleHAiOjE1Mjg4MjE3NjUuNDM0LCJpc3MiOiJodHRwczovL2FwaS5jaXZpYy5jb20vand0IiwiYXVkIjoiQXR0ZXN0ZXJTZXJ2aWNlIiwic3ViIjoiYzhhNjRhODE4NWRlMzNkMTlkZTgwMjFmYmUyMjhkMmE1YTc3YzExMTdkYjc1NDJlZDE0ODM1NGNiZjdkNGVmMSIsImRhdGEiOnsibWV0aG9kIjoiUE9TVCIsInBhdGgiOiJodHRwczovL2Rldi5hcGkuY2l2aWMuY29tL3JlcXVlc3QtYXR0ZXN0YXRpb24tdGJjaC9yZXF1ZXN0QXR0ZXN0YXRpb24ifX0.2Rp8XLTLvzu51raTQRpce8kIiilsMeiPZeWAsuNv5n7hFZGl-ce-fx9DgxsZ0OTaIUgo8frbiGmHjQh0WlUG7A' } + response: { + jwt: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJqdGkiOiIyYzdlNjQ4YS1hNDhmLTQxNTgtOGZmMS02MTY0YzM5OWNlNDMiLCJpYXQiOjE1Mjg4MjE1ODUuNDM0LCJleHAiOjE1Mjg4MjE3NjUuNDM0LCJpc3MiOiJodHRwczovL2FwaS5jaXZpYy5jb20vand0IiwiYXVkIjoiQXR0ZXN0ZXJTZXJ2aWNlIiwic3ViIjoiYzhhNjRhODE4NWRlMzNkMTlkZTgwMjFmYmUyMjhkMmE1YTc3YzExMTdkYjc1NDJlZDE0ODM1NGNiZjdkNGVmMSIsImRhdGEiOnsibWV0aG9kIjoiUE9TVCIsInBhdGgiOiJodHRwczovL2Rldi5hcGkuY2l2aWMuY29tL3JlcXVlc3QtYXR0ZXN0YXRpb24tdGJjaC9yZXF1ZXN0QXR0ZXN0YXRpb24ifX0.2Rp8XLTLvzu51raTQRpce8kIiilsMeiPZeWAsuNv5n7hFZGl-ce-fx9DgxsZ0OTaIUgo8frbiGmHjQh0WlUG7A' + } }, { path: '/requestAttestation', - response: { statusUrl: '/status/372091f0-6e5f-11e8-ab04-8d6f9c9b4e5a' } + response: { + statusUrl: '/status/372091f0-6e5f-11e8-ab04-8d6f9c9b4e5a' + } }, { path: '/requestAttestation', - response: { statusUrl: '/status/372091f0-6e5f-11e8-ab04-8d6f9c9b4e5a' } + response: { + statusUrl: '/status/372091f0-6e5f-11e8-ab04-8d6f9c9b4e5a' + } }, { path: '/status', response: { @@ -49,20 +63,23 @@ function HttpServiceConstructor() { type: 'temporary', network: 'testnet' } - }]; + const res = _.find(responses, r => _.includes(params.url, r.path)); + if (res) { return Promise.resolve(res.response); } + return Promise.reject(); }; + return this; } logger.debug('Using Mock HTTP Service'); const http = new HttpServiceConstructor(); http.request('/status').then(console.log); // eslint-disable-line -logger.debug(`HTTP Service instance ${JSON.stringify(http, null, 2)}`); +logger.debug(`HTTP Service instance ${JSON.stringify(http, null, 2)}`); module.exports = http; \ No newline at end of file diff --git a/dist/es/services/anchorService.js b/dist/es/services/anchorService.js index 49b915d1..7d0aef3f 100644 --- a/dist/es/services/anchorService.js +++ b/dist/es/services/anchorService.js @@ -5,13 +5,21 @@ */ function Anchor(impl) { this.impl = impl; + this.anchor = (label, data, options) => this.impl.anchor(label, data, options); + this.update = tempAnchor => this.impl.update(tempAnchor); + this.verifySignature = subject => this.impl.verifySignature(subject); + this.verifySubjectSignature = subject => this.impl.verifySubjectSignature(subject); + this.verifyAttestation = signature => this.impl.verifyAttestation(signature); + this.revokeAttestation = signature => this.impl.revokeAttestation(signature); + this.isRevoked = signature => this.impl.isRevoked(signature); + return this; } diff --git a/dist/es/services/config.js b/dist/es/services/config.js index 5a6c787a..0d6a580d 100644 --- a/dist/es/services/config.js +++ b/dist/es/services/config.js @@ -1,31 +1,30 @@ const path = require('path'); + const os = require('os'); + const fs = require('fs'); const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined'; - if (process.platform === 'win32') throw new Error(`Unsupported platform: ${process.platform}`); if (process.env.APP_ENV !== 'browser' && !isBrowser) { const CONFIG_FILE = 'config'; - const CONFIG_PATH = { BOX: '/etc/civic', USER: path.join(`${os.homedir()}`, '.civic') }; - const userConfigFile = path.join(CONFIG_PATH.USER, CONFIG_FILE); const boxConfigFile = path.join(CONFIG_PATH.BOX, CONFIG_FILE); - const configFile = fs.existsSync(userConfigFile) ? userConfigFile : boxConfigFile; - /* eslint-disable global-require */ + if (fs.existsSync(userConfigFile)) { require('dotenv').config({ path: configFile }); } /* eslint-ebable global-require */ + } const config = { @@ -39,10 +38,11 @@ const config = { } }, passphrase: process.env.CIVIC_PASSPHRASE, - keychain: { prv: process.env.CIVIC_KEYCHAIN }, + keychain: { + prv: process.env.CIVIC_KEYCHAIN + }, accessToken: process.env.CLIENT_ACCESS_TOKEN, walletId: process.env.CLIENT_WALLET_ID, walletPassphrase: process.env.CLIENT_WALLET_PASSPHRASE }; - module.exports = config; \ No newline at end of file diff --git a/dist/es/services/httpService.js b/dist/es/services/httpService.js index b190f00d..36104de2 100644 --- a/dist/es/services/httpService.js +++ b/dist/es/services/httpService.js @@ -1,15 +1,16 @@ /** * A simple node HTTP services */ -const request = require('request-promise-native'); -// uncomment to debug requests +const request = require('request-promise-native'); // uncomment to debug requests // require('request-debug')(request); + function HttpServiceConstructor() { this.request = async (uri, options) => { const response = await request(uri, options); return response; }; + return this; } diff --git a/dist/es/services/index.js b/dist/es/services/index.js index e0b0b80e..21188164 100644 --- a/dist/es/services/index.js +++ b/dist/es/services/index.js @@ -2,55 +2,65 @@ * Services IoC modules */ const Bottle = require('bottlejs'); -const { CurrentCivicAnchor } = require('./DefaultAnchorServiceImpl.js'); + +const { + CurrentCivicAnchor +} = require('./DefaultAnchorServiceImpl'); + const logger = require('../logger'); + const HttpServiceConstructor = require('./httpService'); + const config = require('./config'); + const SecureRandom = require('../SecureRandom'); + const MiniCryptoManagerImpl = require('./MiniCryptoManagerImpl'); const services = new Bottle(); - /** * Init services with new values to config and http services * @param {*} conf * @param {*} http * @param secureRandom */ + const initServices = (conf, http, secureRandom, cryptoManagerImpl) => { if (http) { services.resetProviders(['Http']); logger.debug('Registering custom HTTP service implementation'); services.factory('Http', () => http); } + if (conf) { services.resetProviders(['Config']); logger.debug('Registering custom Config service implementation'); services.factory('Config', () => conf); } + if (secureRandom) { services.resetProviders(['SecureRandom']); logger.debug('Registering custom SecureRandom service implementation'); services.factory('SecureRandom', () => secureRandom); } + if (cryptoManagerImpl) { services.resetProviders(['CryptoManager']); logger.debug('Registering custom CryptoManager service implementation'); services.factory('CryptoManager', () => cryptoManagerImpl); } + return services; }; services.factory('Config', () => config); - logger.info('Registering request-promise-native as Http service implementation.'); services.service('Http', HttpServiceConstructor); - services.factory('SecureRandom', () => new SecureRandom()); +services.service('AnchorService', CurrentCivicAnchor, 'Config', 'Http'); // The default CryptoManager Implementation -services.service('AnchorService', CurrentCivicAnchor, 'Config', 'Http'); - -// The default CryptoManager Implementation services.service('CryptoManager', MiniCryptoManagerImpl); - -module.exports = { services, initServices }; \ No newline at end of file +module.exports = { + services, + initServices +}; \ No newline at end of file diff --git a/dist/es/timeHelper.js b/dist/es/timeHelper.js index 76184995..6b5878e5 100644 --- a/dist/es/timeHelper.js +++ b/dist/es/timeHelper.js @@ -6,28 +6,26 @@ const unitMapper = { w: 'w', d: 'd' }; - /** * Convert a delta string like "21y" to a moment Duration object * @param delta * @return {moment.Duration} */ + const timeDeltaToMomentDuration = delta => { const matched = delta.match(/(-?\d+)(\w)/); - if (!matched) throw new Error(`Invalid time delta ${delta}`); - const [, amount, unit] = matched; - return moment.duration(parseInt(amount, 10), unitMapper[unit]); }; - /** * Given a time delta like "-21y", apply it to the passed in date object, or the current time * @param delta String * @param date Date * @return {Date} */ + + const applyDeltaToDate = (delta, date = new Date()) => moment(date).add(timeDeltaToMomentDuration(delta)).toDate(); module.exports = { diff --git a/dist/es/uca/UCA.js b/dist/es/uca/UCA.js new file mode 100644 index 00000000..3b8b7fa0 --- /dev/null +++ b/dist/es/uca/UCA.js @@ -0,0 +1,19 @@ +const { + UserCollectableAttribute: BaseUCA +} = require('@identity.com/uca'); + +const { + schemaLoader +} = require('../schemas/jsonSchema'); + +class UserCollectableAttribute extends BaseUCA { + static async create(identifier, value, version) { + await schemaLoader.loadSchemaFromTitle(identifier); + return new UserCollectableAttribute(identifier, value, version, schemaLoader.ucaDefinitions); + } + +} + +module.exports = { + UserCollectableAttribute +}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 134b66f8..6ff6000a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@identity.com/credential-commons", - "version": "3.0.0-beta.6", + "version": "3.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1207,10 +1207,15 @@ "minimist": "^1.2.0" } }, + "@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==" + }, "@dabh/diagnostics": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", - "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", "requires": { "colorspace": "1.1.x", "enabled": "2.0.x", @@ -3117,24 +3122,11 @@ } }, "ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-1.6.1.tgz", + "integrity": "sha512-4CjkH20If1lhR5CGtqkrVg3bbOtFEG80X9v6jDOIUhbzzbB+UzPBGy8GQhUNVZ0yvMHdMpawCOcy5ydGMsagGQ==", "requires": { - "ajv": "^8.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz", - "integrity": "sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw==", - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - } + "ajv": "^7.0.0" } }, "ansi-colors": { @@ -3289,9 +3281,9 @@ "dev": true }, "async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==" + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" }, "asynckit": { "version": "0.4.0", @@ -3924,7 +3916,7 @@ "bigi": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/bigi/-/bigi-1.4.2.tgz", - "integrity": "sha1-nGZalfiLiwj8Bc/XMfVhhZ1yWCU=" + "integrity": "sha512-ddkU+dFIuEIW8lE7ZwdIAf2UPoM90eaprg5m3YXAVVTmKlqV/9BX4A2M8BOK2yOq6/VgZFVhK6QAxJebhlbhzw==" }, "binary-extensions": { "version": "2.2.0", @@ -3936,7 +3928,7 @@ "bip66": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz", - "integrity": "sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI=", + "integrity": "sha512-nemMHz95EmS38a26XbbdxIYj5csHd3RMP3H5bwQknX0WYHF01qhpufP42mLOwVICuH2JmhIhXiWs89MfUGL7Xw==", "requires": { "safe-buffer": "^5.0.1" } @@ -4315,19 +4307,14 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "color-string": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.0.tgz", - "integrity": "sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", "requires": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" - }, "colorspace": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", @@ -4365,7 +4352,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "confusing-browser-globals": { @@ -5833,9 +5820,9 @@ } }, "fecha": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz", - "integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q==" + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" }, "figlet": { "version": "1.5.2", @@ -9760,13 +9747,10 @@ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, "json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true }, "jsonparse": { "version": "1.3.1", @@ -9919,14 +9903,14 @@ "dev": true }, "logform": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.3.2.tgz", - "integrity": "sha512-V6JiPThZzTsbVRspNO6TmHkR99oqYTs8fivMBYQkjZj6rxW92KxtDCPE6IkAk1DNBnYKNkjm4jYBm6JDUcyhOA==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.4.2.tgz", + "integrity": "sha512-W4c9himeAwXEdZ05dQNerhFz2XG80P9Oj0loPUMV23VC2it0orMHQhJm4hdnnor3rd1HsGf6a2lPwBM1zeXHGw==", "requires": { - "colors": "1.4.0", + "@colors/colors": "1.5.0", "fecha": "^4.2.0", "ms": "^2.1.1", - "safe-stable-stringify": "^1.1.0", + "safe-stable-stringify": "^2.3.1", "triple-beam": "^1.3.0" } }, @@ -10015,7 +9999,7 @@ "merkle-lib": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/merkle-lib/-/merkle-lib-2.0.10.tgz", - "integrity": "sha1-grjbrnXieneFOItz+ddyXQ9vMyY=" + "integrity": "sha512-XrNQvUbn1DL5hKNe46Ccs+Tu3/PYOlrcZILuGUhb95oKBPjc/nmIC8D462PQkipVDGKRvwhn+QFg2cCdIvmDJA==" }, "merkle-tools": { "version": "1.4.1", @@ -10059,9 +10043,9 @@ "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -10379,7 +10363,8 @@ "dependencies": { "JSONStream": { "version": "1.3.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", "dev": true, "requires": { "jsonparse": "^1.2.0", @@ -10388,12 +10373,14 @@ }, "abbrev": { "version": "1.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, "agent-base": { "version": "4.3.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", "dev": true, "requires": { "es6-promisify": "^5.0.0" @@ -10401,7 +10388,8 @@ }, "agentkeepalive": { "version": "3.5.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz", + "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", "dev": true, "requires": { "humanize-ms": "^1.2.1" @@ -10409,7 +10397,8 @@ }, "ansi-align": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", + "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", "dev": true, "requires": { "string-width": "^2.0.0" @@ -10417,12 +10406,14 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, "ansi-styles": { "version": "3.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -10430,27 +10421,32 @@ }, "ansicolors": { "version": "0.3.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", + "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=", "dev": true }, "ansistyles": { "version": "0.1.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/ansistyles/-/ansistyles-0.1.3.tgz", + "integrity": "sha1-XeYEFb2gcbs3EnhUyGT0GyMlRTk=", "dev": true }, "aproba": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", "dev": true }, "archy": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", "dev": true }, "are-we-there-yet": { "version": "1.1.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", + "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", "dev": true, "requires": { "delegates": "^1.0.0", @@ -10459,7 +10455,8 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -10473,7 +10470,8 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -10483,12 +10481,14 @@ }, "asap": { "version": "2.0.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", "dev": true }, "asn1": { "version": "0.2.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", "dev": true, "requires": { "safer-buffer": "~2.1.0" @@ -10496,32 +10496,38 @@ }, "assert-plus": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", "dev": true }, "asynckit": { "version": "0.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, "aws-sign2": { "version": "0.7.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", "dev": true }, "aws4": { "version": "1.8.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", "dev": true }, "balanced-match": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, "bcrypt-pbkdf": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "dev": true, "optional": true, "requires": { @@ -10530,7 +10536,8 @@ }, "bin-links": { "version": "1.1.8", - "bundled": true, + "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-1.1.8.tgz", + "integrity": "sha512-KgmVfx+QqggqP9dA3iIc5pA4T1qEEEL+hOhOhNPaUm77OTrJoOXE/C05SJLNJe6m/2wUK7F1tDSou7n5TfCDzQ==", "dev": true, "requires": { "bluebird": "^3.5.3", @@ -10543,12 +10550,14 @@ }, "bluebird": { "version": "3.5.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", + "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", "dev": true }, "boxen": { "version": "1.3.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", + "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", "dev": true, "requires": { "ansi-align": "^2.0.0", @@ -10562,7 +10571,8 @@ }, "brace-expansion": { "version": "1.1.11", - "bundled": true, + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { "balanced-match": "^1.0.0", @@ -10571,27 +10581,32 @@ }, "buffer-from": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz", + "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==", "dev": true }, "builtins": { "version": "1.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=", "dev": true }, "byline": { "version": "5.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", + "integrity": "sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE=", "dev": true }, "byte-size": { "version": "5.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/byte-size/-/byte-size-5.0.1.tgz", + "integrity": "sha512-/XuKeqWocKsYa/cBY1YbSJSWWqTi4cFgr9S6OyM7PBaPbr9zvNGwWP33vt0uqGhwDdN+y3yhbXVILEUpnwEWGw==", "dev": true }, "cacache": { "version": "12.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", + "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", "dev": true, "requires": { "bluebird": "^3.5.5", @@ -10613,27 +10628,32 @@ }, "call-limit": { "version": "1.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/call-limit/-/call-limit-1.1.1.tgz", + "integrity": "sha512-5twvci5b9eRBw2wCfPtN0GmlR2/gadZqyFpPhOK6CvMFoFgA+USnZ6Jpu1lhG9h85pQ3Ouil3PfXWRD4EUaRiQ==", "dev": true }, "camelcase": { "version": "4.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", "dev": true }, "capture-stack-trace": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz", + "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=", "dev": true }, "caseless": { "version": "0.12.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", "dev": true }, "chalk": { "version": "2.4.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -10643,17 +10663,20 @@ }, "chownr": { "version": "1.1.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "dev": true }, "ci-info": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", "dev": true }, "cidr-regex": { "version": "2.0.10", - "bundled": true, + "resolved": "https://registry.npmjs.org/cidr-regex/-/cidr-regex-2.0.10.tgz", + "integrity": "sha512-sB3ogMQXWvreNPbJUZMRApxuRYd+KoIo4RGQ81VatjmMW6WJPo+IJZ2846FGItr9VzKo5w7DXzijPLGtSd0N3Q==", "dev": true, "requires": { "ip-regex": "^2.1.0" @@ -10661,12 +10684,14 @@ }, "cli-boxes": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", + "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", "dev": true }, "cli-columns": { "version": "3.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/cli-columns/-/cli-columns-3.1.2.tgz", + "integrity": "sha1-ZzLZcpee/CrkRKHwjgj6E5yWoY4=", "dev": true, "requires": { "string-width": "^2.0.0", @@ -10675,7 +10700,8 @@ }, "cli-table3": { "version": "0.5.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz", + "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==", "dev": true, "requires": { "colors": "^1.1.2", @@ -10685,7 +10711,8 @@ }, "cliui": { "version": "5.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", "dev": true, "requires": { "string-width": "^3.1.0", @@ -10695,17 +10722,20 @@ "dependencies": { "ansi-regex": { "version": "4.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, "string-width": { "version": "3.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { "emoji-regex": "^7.0.1", @@ -10715,7 +10745,8 @@ }, "strip-ansi": { "version": "5.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { "ansi-regex": "^4.1.0" @@ -10725,12 +10756,14 @@ }, "clone": { "version": "1.0.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", "dev": true }, "cmd-shim": { "version": "3.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-3.0.3.tgz", + "integrity": "sha512-DtGg+0xiFhQIntSBRzL2fRQBnmtAVwXIDo4Qq46HPpObYquxMaZS4sb82U9nH91qJrlosC1wa9gwr0QyL/HypA==", "dev": true, "requires": { "graceful-fs": "^4.1.2", @@ -10739,12 +10772,14 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true }, "color-convert": { "version": "1.9.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", "dev": true, "requires": { "color-name": "^1.1.1" @@ -10752,18 +10787,21 @@ }, "color-name": { "version": "1.1.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, "colors": { "version": "1.3.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", + "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==", "dev": true, "optional": true }, "columnify": { "version": "1.5.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.5.4.tgz", + "integrity": "sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs=", "dev": true, "requires": { "strip-ansi": "^3.0.0", @@ -10772,7 +10810,8 @@ }, "combined-stream": { "version": "1.0.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", "dev": true, "requires": { "delayed-stream": "~1.0.0" @@ -10780,12 +10819,14 @@ }, "concat-map": { "version": "0.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, "concat-stream": { "version": "1.6.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -10796,7 +10837,8 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -10810,7 +10852,8 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -10820,7 +10863,8 @@ }, "config-chain": { "version": "1.1.12", - "bundled": true, + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", + "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", "dev": true, "requires": { "ini": "^1.3.4", @@ -10829,7 +10873,8 @@ }, "configstore": { "version": "3.1.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.5.tgz", + "integrity": "sha512-nlOhI4+fdzoK5xmJ+NY+1gZK56bwEaWZr8fYuXohZ9Vkc1o3a4T/R3M+yE/w7x/ZVJ1zF8c+oaOvF0dztdUgmA==", "dev": true, "requires": { "dot-prop": "^4.2.1", @@ -10842,12 +10887,14 @@ }, "console-control-strings": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "dev": true }, "copy-concurrently": { "version": "1.0.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", "dev": true, "requires": { "aproba": "^1.1.1", @@ -10860,24 +10907,28 @@ "dependencies": { "aproba": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true }, "iferr": { "version": "0.1.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", "dev": true } } }, "core-util-is": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, "create-error-class": { "version": "3.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", + "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", "dev": true, "requires": { "capture-stack-trace": "^1.0.0" @@ -10885,7 +10936,8 @@ }, "cross-spawn": { "version": "5.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { "lru-cache": "^4.0.1", @@ -10895,7 +10947,8 @@ "dependencies": { "lru-cache": { "version": "4.1.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", "dev": true, "requires": { "pseudomap": "^1.0.2", @@ -10904,24 +10957,28 @@ }, "yallist": { "version": "2.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", "dev": true } } }, "crypto-random-string": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", + "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", "dev": true }, "cyclist": { "version": "0.2.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", + "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", "dev": true }, "dashdash": { "version": "1.14.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "dev": true, "requires": { "assert-plus": "^1.0.0" @@ -10929,7 +10986,8 @@ }, "debug": { "version": "3.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" @@ -10937,34 +10995,40 @@ "dependencies": { "ms": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true } } }, "debuglog": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", + "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=", "dev": true }, "decamelize": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, "decode-uri-component": { "version": "0.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", "dev": true }, "deep-extend": { "version": "0.6.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true }, "defaults": { "version": "1.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", "dev": true, "requires": { "clone": "^1.0.2" @@ -10972,7 +11036,8 @@ }, "define-properties": { "version": "1.1.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "dev": true, "requires": { "object-keys": "^1.0.12" @@ -10980,27 +11045,32 @@ }, "delayed-stream": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, "delegates": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true }, "detect-indent": { "version": "5.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz", + "integrity": "sha1-OHHMCmoALow+Wzz38zYmRnXwa50=", "dev": true }, "detect-newline": { "version": "2.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", "dev": true }, "dezalgo": { "version": "1.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", + "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=", "dev": true, "requires": { "asap": "^2.0.0", @@ -11009,7 +11079,8 @@ }, "dot-prop": { "version": "4.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz", + "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==", "dev": true, "requires": { "is-obj": "^1.0.0" @@ -11017,17 +11088,20 @@ }, "dotenv": { "version": "5.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-5.0.1.tgz", + "integrity": "sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow==", "dev": true }, "duplexer3": { "version": "0.1.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", "dev": true }, "duplexify": { "version": "3.6.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz", + "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", "dev": true, "requires": { "end-of-stream": "^1.0.0", @@ -11038,7 +11112,8 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -11052,7 +11127,8 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -11062,7 +11138,8 @@ }, "ecc-jsbn": { "version": "0.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "dev": true, "optional": true, "requires": { @@ -11072,17 +11149,20 @@ }, "editor": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/editor/-/editor-1.0.0.tgz", + "integrity": "sha1-YMf4e9YrzGqJT6jM1q+3gjok90I=", "dev": true }, "emoji-regex": { "version": "7.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, "encoding": { "version": "0.1.12", - "bundled": true, + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", "dev": true, "requires": { "iconv-lite": "~0.4.13" @@ -11090,7 +11170,8 @@ }, "end-of-stream": { "version": "1.4.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", "dev": true, "requires": { "once": "^1.4.0" @@ -11098,17 +11179,20 @@ }, "env-paths": { "version": "2.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz", + "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==", "dev": true }, "err-code": { "version": "1.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", + "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=", "dev": true }, "errno": { "version": "0.1.7", - "bundled": true, + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", "dev": true, "requires": { "prr": "~1.0.1" @@ -11116,7 +11200,8 @@ }, "es-abstract": { "version": "1.12.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz", + "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", "dev": true, "requires": { "es-to-primitive": "^1.1.1", @@ -11128,7 +11213,8 @@ }, "es-to-primitive": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", "dev": true, "requires": { "is-callable": "^1.1.4", @@ -11138,12 +11224,14 @@ }, "es6-promise": { "version": "4.2.8", - "bundled": true, + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", "dev": true }, "es6-promisify": { "version": "5.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", "dev": true, "requires": { "es6-promise": "^4.0.3" @@ -11151,12 +11239,14 @@ }, "escape-string-regexp": { "version": "1.0.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, "execa": { "version": "0.7.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "dev": true, "requires": { "cross-spawn": "^5.0.1", @@ -11170,39 +11260,46 @@ "dependencies": { "get-stream": { "version": "3.0.0", - "bundled": true, + "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true } } }, "extend": { "version": "3.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, "extsprintf": { "version": "1.3.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", "dev": true }, "fast-json-stable-stringify": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", "dev": true }, "figgy-pudding": { "version": "3.5.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", + "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", "dev": true }, "find-npm-prefix": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/find-npm-prefix/-/find-npm-prefix-1.0.2.tgz", + "integrity": "sha512-KEftzJ+H90x6pcKtdXZEPsQse8/y/UnvzRKrOSQFprnrGaFuJ62fVkP34Iu2IYuMvyauCyoLTNkJZgrrGA2wkA==", "dev": true }, "flush-write-stream": { "version": "1.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz", + "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", "dev": true, "requires": { "inherits": "^2.0.1", @@ -11211,7 +11308,8 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -11225,7 +11323,8 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -11235,12 +11334,14 @@ }, "forever-agent": { "version": "0.6.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", "dev": true }, "form-data": { "version": "2.3.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", + "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", "dev": true, "requires": { "asynckit": "^0.4.0", @@ -11250,7 +11351,8 @@ }, "from2": { "version": "2.3.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", "dev": true, "requires": { "inherits": "^2.0.1", @@ -11259,7 +11361,8 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -11273,7 +11376,8 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -11283,7 +11387,8 @@ }, "fs-minipass": { "version": "1.2.7", - "bundled": true, + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", "dev": true, "requires": { "minipass": "^2.6.0" @@ -11291,7 +11396,8 @@ "dependencies": { "minipass": { "version": "2.9.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", "dev": true, "requires": { "safe-buffer": "^5.1.2", @@ -11302,7 +11408,8 @@ }, "fs-vacuum": { "version": "1.2.10", - "bundled": true, + "resolved": "https://registry.npmjs.org/fs-vacuum/-/fs-vacuum-1.2.10.tgz", + "integrity": "sha1-t2Kb7AekAxolSP35n17PHMizHjY=", "dev": true, "requires": { "graceful-fs": "^4.1.2", @@ -11312,7 +11419,8 @@ }, "fs-write-stream-atomic": { "version": "1.0.10", - "bundled": true, + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", "dev": true, "requires": { "graceful-fs": "^4.1.2", @@ -11323,12 +11431,14 @@ "dependencies": { "iferr": { "version": "0.1.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", "dev": true }, "readable-stream": { "version": "2.3.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -11342,7 +11452,8 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -11352,17 +11463,20 @@ }, "fs.realpath": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, "function-bind": { "version": "1.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, "gauge": { "version": "2.7.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "requires": { "aproba": "^1.0.3", @@ -11377,12 +11491,14 @@ "dependencies": { "aproba": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true }, "string-width": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { "code-point-at": "^1.0.0", @@ -11394,12 +11510,14 @@ }, "genfun": { "version": "5.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz", + "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==", "dev": true }, "gentle-fs": { "version": "2.3.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/gentle-fs/-/gentle-fs-2.3.1.tgz", + "integrity": "sha512-OlwBBwqCFPcjm33rF2BjW+Pr6/ll2741l+xooiwTCeaX2CA1ZuclavyMBe0/KlR21/XGsgY6hzEQZ15BdNa13Q==", "dev": true, "requires": { "aproba": "^1.1.2", @@ -11417,24 +11535,28 @@ "dependencies": { "aproba": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true }, "iferr": { "version": "0.1.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", "dev": true } } }, "get-caller-file": { "version": "2.0.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, "get-stream": { "version": "4.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, "requires": { "pump": "^3.0.0" @@ -11442,7 +11564,8 @@ }, "getpass": { "version": "0.1.7", - "bundled": true, + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "dev": true, "requires": { "assert-plus": "^1.0.0" @@ -11450,7 +11573,8 @@ }, "glob": { "version": "7.1.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -11463,7 +11587,8 @@ }, "global-dirs": { "version": "0.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", "dev": true, "requires": { "ini": "^1.3.4" @@ -11471,7 +11596,8 @@ }, "got": { "version": "6.7.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", + "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", "dev": true, "requires": { "create-error-class": "^3.0.0", @@ -11489,24 +11615,28 @@ "dependencies": { "get-stream": { "version": "3.0.0", - "bundled": true, + "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true } } }, "graceful-fs": { "version": "4.2.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", "dev": true }, "har-schema": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", "dev": true }, "har-validator": { "version": "5.1.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", "dev": true, "requires": { "ajv": "^6.12.3", @@ -11515,7 +11645,8 @@ "dependencies": { "ajv": { "version": "6.12.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -11526,19 +11657,22 @@ }, "fast-deep-equal": { "version": "3.1.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, "json-schema-traverse": { "version": "0.4.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true } } }, "has": { "version": "1.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { "function-bind": "^1.1.1" @@ -11546,32 +11680,38 @@ }, "has-flag": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, "has-symbols": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", "dev": true }, "has-unicode": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true }, "hosted-git-info": { "version": "2.8.9", - "bundled": true, + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, "http-cache-semantics": { "version": "3.8.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", + "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", "dev": true }, "http-proxy-agent": { "version": "2.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", + "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", "dev": true, "requires": { "agent-base": "4", @@ -11580,7 +11720,8 @@ }, "http-signature": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "dev": true, "requires": { "assert-plus": "^1.0.0", @@ -11590,7 +11731,8 @@ }, "https-proxy-agent": { "version": "2.2.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", "dev": true, "requires": { "agent-base": "^4.3.0", @@ -11599,7 +11741,8 @@ }, "humanize-ms": { "version": "1.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", "dev": true, "requires": { "ms": "^2.0.0" @@ -11607,7 +11750,8 @@ }, "iconv-lite": { "version": "0.4.23", - "bundled": true, + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" @@ -11615,12 +11759,14 @@ }, "iferr": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/iferr/-/iferr-1.0.2.tgz", + "integrity": "sha512-9AfeLfji44r5TKInjhz3W9DyZI1zR1JAf2hVBMGhddAKPqBsupb89jGfbCTHIGZd6fGZl9WlHdn4AObygyMKwg==", "dev": true }, "ignore-walk": { "version": "3.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", + "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", "dev": true, "requires": { "minimatch": "^3.0.4" @@ -11628,22 +11774,26 @@ }, "import-lazy": { "version": "2.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", "dev": true }, "imurmurhash": { "version": "0.1.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, "infer-owner": { "version": "1.0.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", "dev": true }, "inflight": { "version": "1.0.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { "once": "^1.3.0", @@ -11652,17 +11802,20 @@ }, "inherits": { "version": "2.0.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "ini": { "version": "1.3.8", - "bundled": true, + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, "init-package-json": { "version": "1.10.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/init-package-json/-/init-package-json-1.10.3.tgz", + "integrity": "sha512-zKSiXKhQveNteyhcj1CoOP8tqp1QuxPIPBl8Bid99DGLFqA1p87M6lNgfjJHSBoWJJlidGOv5rWjyYKEB3g2Jw==", "dev": true, "requires": { "glob": "^7.1.1", @@ -11677,22 +11830,26 @@ }, "ip": { "version": "1.1.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", "dev": true }, "ip-regex": { "version": "2.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", "dev": true }, "is-callable": { "version": "1.1.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", "dev": true }, "is-ci": { "version": "1.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", + "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", "dev": true, "requires": { "ci-info": "^1.5.0" @@ -11700,14 +11857,16 @@ "dependencies": { "ci-info": { "version": "1.6.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", + "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", "dev": true } } }, "is-cidr": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-cidr/-/is-cidr-3.0.0.tgz", + "integrity": "sha512-8Xnnbjsb0x462VoYiGlhEi+drY8SFwrHiSYuzc/CEwco55vkehTaxAyIjEdpi3EMvLPPJAJi9FlzP+h+03gp0Q==", "dev": true, "requires": { "cidr-regex": "^2.0.10" @@ -11715,12 +11874,14 @@ }, "is-date-object": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", "dev": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { "number-is-nan": "^1.0.0" @@ -11728,7 +11889,8 @@ }, "is-installed-globally": { "version": "0.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", + "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", "dev": true, "requires": { "global-dirs": "^0.1.0", @@ -11737,17 +11899,20 @@ }, "is-npm": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", + "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=", "dev": true }, "is-obj": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", "dev": true }, "is-path-inside": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", "dev": true, "requires": { "path-is-inside": "^1.0.1" @@ -11755,12 +11920,14 @@ }, "is-redirect": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", + "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", "dev": true }, "is-regex": { "version": "1.0.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", "dev": true, "requires": { "has": "^1.0.1" @@ -11768,17 +11935,20 @@ }, "is-retry-allowed": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", + "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", "dev": true }, "is-stream": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, "is-symbol": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", "dev": true, "requires": { "has-symbols": "^1.0.0" @@ -11786,53 +11956,63 @@ }, "is-typedarray": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, "isarray": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, "isexe": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, "isstream": { "version": "0.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", "dev": true }, "jsbn": { "version": "0.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "dev": true, "optional": true }, "json-parse-better-errors": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, "json-schema": { "version": "0.2.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", "dev": true }, "json-stringify-safe": { "version": "5.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", "dev": true }, "jsonparse": { "version": "1.3.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", "dev": true }, "jsprim": { "version": "1.4.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", "dev": true, "requires": { "assert-plus": "1.0.0", @@ -11843,7 +12023,8 @@ }, "latest-version": { "version": "3.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", + "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", "dev": true, "requires": { "package-json": "^4.0.0" @@ -11851,12 +12032,14 @@ }, "lazy-property": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/lazy-property/-/lazy-property-1.0.0.tgz", + "integrity": "sha1-hN3Es3Bnm6i9TNz6TAa0PVcREUc=", "dev": true }, "libcipm": { "version": "4.0.8", - "bundled": true, + "resolved": "https://registry.npmjs.org/libcipm/-/libcipm-4.0.8.tgz", + "integrity": "sha512-IN3hh2yDJQtZZ5paSV4fbvJg4aHxCCg5tcZID/dSVlTuUiWktsgaldVljJv6Z5OUlYspx6xQkbR0efNodnIrOA==", "dev": true, "requires": { "bin-links": "^1.1.2", @@ -11878,7 +12061,8 @@ }, "libnpm": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/libnpm/-/libnpm-3.0.1.tgz", + "integrity": "sha512-d7jU5ZcMiTfBqTUJVZ3xid44fE5ERBm9vBnmhp2ECD2Ls+FNXWxHSkO7gtvrnbLO78gwPdNPz1HpsF3W4rjkBQ==", "dev": true, "requires": { "bin-links": "^1.1.2", @@ -11905,7 +12089,8 @@ }, "libnpmaccess": { "version": "3.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/libnpmaccess/-/libnpmaccess-3.0.2.tgz", + "integrity": "sha512-01512AK7MqByrI2mfC7h5j8N9V4I7MHJuk9buo8Gv+5QgThpOgpjB7sQBDDkeZqRteFb1QM/6YNdHfG7cDvfAQ==", "dev": true, "requires": { "aproba": "^2.0.0", @@ -11916,7 +12101,8 @@ }, "libnpmconfig": { "version": "1.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/libnpmconfig/-/libnpmconfig-1.2.1.tgz", + "integrity": "sha512-9esX8rTQAHqarx6qeZqmGQKBNZR5OIbl/Ayr0qQDy3oXja2iFVQQI81R6GZ2a02bSNZ9p3YOGX1O6HHCb1X7kA==", "dev": true, "requires": { "figgy-pudding": "^3.5.1", @@ -11926,7 +12112,8 @@ "dependencies": { "find-up": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { "locate-path": "^3.0.0" @@ -11934,7 +12121,8 @@ }, "locate-path": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { "p-locate": "^3.0.0", @@ -11943,7 +12131,8 @@ }, "p-limit": { "version": "2.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -11951,7 +12140,8 @@ }, "p-locate": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { "p-limit": "^2.0.0" @@ -11959,14 +12149,16 @@ }, "p-try": { "version": "2.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true } } }, "libnpmhook": { "version": "5.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/libnpmhook/-/libnpmhook-5.0.3.tgz", + "integrity": "sha512-UdNLMuefVZra/wbnBXECZPefHMGsVDTq5zaM/LgKNE9Keyl5YXQTnGAzEo+nFOpdRqTWI9LYi4ApqF9uVCCtuA==", "dev": true, "requires": { "aproba": "^2.0.0", @@ -11977,7 +12169,8 @@ }, "libnpmorg": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/libnpmorg/-/libnpmorg-1.0.1.tgz", + "integrity": "sha512-0sRUXLh+PLBgZmARvthhYXQAWn0fOsa6T5l3JSe2n9vKG/lCVK4nuG7pDsa7uMq+uTt2epdPK+a2g6btcY11Ww==", "dev": true, "requires": { "aproba": "^2.0.0", @@ -11988,7 +12181,8 @@ }, "libnpmpublish": { "version": "1.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/libnpmpublish/-/libnpmpublish-1.1.2.tgz", + "integrity": "sha512-2yIwaXrhTTcF7bkJKIKmaCV9wZOALf/gsTDxVSu/Gu/6wiG3fA8ce8YKstiWKTxSFNC0R7isPUb6tXTVFZHt2g==", "dev": true, "requires": { "aproba": "^2.0.0", @@ -12004,7 +12198,8 @@ }, "libnpmsearch": { "version": "2.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/libnpmsearch/-/libnpmsearch-2.0.2.tgz", + "integrity": "sha512-VTBbV55Q6fRzTdzziYCr64+f8AopQ1YZ+BdPOv16UegIEaE8C0Kch01wo4s3kRTFV64P121WZJwgmBwrq68zYg==", "dev": true, "requires": { "figgy-pudding": "^3.5.1", @@ -12014,7 +12209,8 @@ }, "libnpmteam": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/libnpmteam/-/libnpmteam-1.0.2.tgz", + "integrity": "sha512-p420vM28Us04NAcg1rzgGW63LMM6rwe+6rtZpfDxCcXxM0zUTLl7nPFEnRF3JfFBF5skF/yuZDUthTsHgde8QA==", "dev": true, "requires": { "aproba": "^2.0.0", @@ -12025,7 +12221,8 @@ }, "libnpx": { "version": "10.2.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/libnpx/-/libnpx-10.2.4.tgz", + "integrity": "sha512-BPc0D1cOjBeS8VIBKUu5F80s6njm0wbVt7CsGMrIcJ+SI7pi7V0uVPGpEMH9H5L8csOcclTxAXFE2VAsJXUhfA==", "dev": true, "requires": { "dotenv": "^5.0.1", @@ -12040,7 +12237,8 @@ }, "lock-verify": { "version": "2.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/lock-verify/-/lock-verify-2.1.0.tgz", + "integrity": "sha512-vcLpxnGvrqisKvLQ2C2v0/u7LVly17ak2YSgoK4PrdsYBXQIax19vhKiLfvKNFx7FRrpTnitrpzF/uuCMuorIg==", "dev": true, "requires": { "npm-package-arg": "^6.1.0", @@ -12049,7 +12247,8 @@ }, "lockfile": { "version": "1.0.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.4.tgz", + "integrity": "sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==", "dev": true, "requires": { "signal-exit": "^3.0.2" @@ -12057,12 +12256,14 @@ }, "lodash._baseindexof": { "version": "3.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz", + "integrity": "sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw=", "dev": true }, "lodash._baseuniq": { "version": "4.6.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz", + "integrity": "sha1-DrtE5FaBSveQXGIS+iybLVG4Qeg=", "dev": true, "requires": { "lodash._createset": "~4.0.0", @@ -12071,17 +12272,20 @@ }, "lodash._bindcallback": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz", + "integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4=", "dev": true }, "lodash._cacheindexof": { "version": "3.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz", + "integrity": "sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI=", "dev": true }, "lodash._createcache": { "version": "3.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/lodash._createcache/-/lodash._createcache-3.1.2.tgz", + "integrity": "sha1-VtagZAF2JeeevKa4AY4XRAvc8JM=", "dev": true, "requires": { "lodash._getnative": "^3.0.0" @@ -12089,52 +12293,62 @@ }, "lodash._createset": { "version": "4.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/lodash._createset/-/lodash._createset-4.0.3.tgz", + "integrity": "sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY=", "dev": true }, "lodash._getnative": { "version": "3.9.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", "dev": true }, "lodash._root": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", + "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", "dev": true }, "lodash.clonedeep": { "version": "4.5.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", "dev": true }, "lodash.restparam": { "version": "3.6.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", + "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=", "dev": true }, "lodash.union": { "version": "4.6.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=", "dev": true }, "lodash.uniq": { "version": "4.5.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", "dev": true }, "lodash.without": { "version": "4.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/lodash.without/-/lodash.without-4.4.0.tgz", + "integrity": "sha1-PNRXSgC2e643OpS3SHcmQFB7eqw=", "dev": true }, "lowercase-keys": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", "dev": true }, "lru-cache": { "version": "5.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "requires": { "yallist": "^3.0.2" @@ -12142,7 +12356,8 @@ }, "make-dir": { "version": "1.3.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", "dev": true, "requires": { "pify": "^3.0.0" @@ -12150,7 +12365,8 @@ }, "make-fetch-happen": { "version": "5.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz", + "integrity": "sha512-07JHC0r1ykIoruKO8ifMXu+xEU8qOXDFETylktdug6vJDACnP+HKevOu3PXyNPzFyTSlz8vrBYlBO1JZRe8Cag==", "dev": true, "requires": { "agentkeepalive": "^3.4.1", @@ -12168,17 +12384,20 @@ }, "meant": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/meant/-/meant-1.0.2.tgz", + "integrity": "sha512-KN+1uowN/NK+sT/Lzx7WSGIj2u+3xe5n2LbwObfjOhPZiA+cCfCm6idVl0RkEfjThkw5XJ96CyRcanq6GmKtUg==", "dev": true }, "mime-db": { "version": "1.35.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz", + "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==", "dev": true }, "mime-types": { "version": "2.1.19", - "bundled": true, + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz", + "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", "dev": true, "requires": { "mime-db": "~1.35.0" @@ -12186,7 +12405,8 @@ }, "minimatch": { "version": "3.0.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -12194,12 +12414,14 @@ }, "minimist": { "version": "1.2.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, "minizlib": { "version": "1.3.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", "dev": true, "requires": { "minipass": "^2.9.0" @@ -12207,7 +12429,8 @@ "dependencies": { "minipass": { "version": "2.9.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", "dev": true, "requires": { "safe-buffer": "^5.1.2", @@ -12218,7 +12441,8 @@ }, "mississippi": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", "dev": true, "requires": { "concat-stream": "^1.5.0", @@ -12235,7 +12459,8 @@ }, "mkdirp": { "version": "0.5.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, "requires": { "minimist": "^1.2.5" @@ -12243,14 +12468,16 @@ "dependencies": { "minimist": { "version": "1.2.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true } } }, "move-concurrently": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", "dev": true, "requires": { "aproba": "^1.1.1", @@ -12263,24 +12490,28 @@ "dependencies": { "aproba": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true } } }, "ms": { "version": "2.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true }, "mute-stream": { "version": "0.0.7", - "bundled": true, + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, "node-fetch-npm": { "version": "2.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz", + "integrity": "sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw==", "dev": true, "requires": { "encoding": "^0.1.11", @@ -12290,7 +12521,8 @@ }, "node-gyp": { "version": "5.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-5.1.0.tgz", + "integrity": "sha512-OUTryc5bt/P8zVgNUmC6xdXiDJxLMAW8cF5tLQOT9E5sOQj+UeQxnnPy74K3CLCa/SOjjBlbuzDLR8ANwA+wmw==", "dev": true, "requires": { "env-paths": "^2.2.0", @@ -12308,7 +12540,8 @@ }, "nopt": { "version": "4.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", "dev": true, "requires": { "abbrev": "1", @@ -12317,7 +12550,8 @@ }, "normalize-package-data": { "version": "2.5.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, "requires": { "hosted-git-info": "^2.1.4", @@ -12328,7 +12562,8 @@ "dependencies": { "resolve": { "version": "1.10.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", "dev": true, "requires": { "path-parse": "^1.0.6" @@ -12338,7 +12573,8 @@ }, "npm-audit-report": { "version": "1.3.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-audit-report/-/npm-audit-report-1.3.3.tgz", + "integrity": "sha512-8nH/JjsFfAWMvn474HB9mpmMjrnKb1Hx/oTAdjv4PT9iZBvBxiZ+wtDUapHCJwLqYGQVPaAfs+vL5+5k9QndXw==", "dev": true, "requires": { "cli-table3": "^0.5.0", @@ -12347,7 +12583,8 @@ }, "npm-bundled": { "version": "1.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", + "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", "dev": true, "requires": { "npm-normalize-package-bin": "^1.0.1" @@ -12355,12 +12592,14 @@ }, "npm-cache-filename": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-cache-filename/-/npm-cache-filename-1.0.2.tgz", + "integrity": "sha1-3tMGxbC/yHCp6fr4I7xfKD4FrhE=", "dev": true }, "npm-install-checks": { "version": "3.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-3.0.2.tgz", + "integrity": "sha512-E4kzkyZDIWoin6uT5howP8VDvkM+E8IQDcHAycaAxMbwkqhIg5eEYALnXOl3Hq9MrkdQB/2/g1xwBINXdKSRkg==", "dev": true, "requires": { "semver": "^2.3.0 || 3.x || 4 || 5" @@ -12368,7 +12607,8 @@ }, "npm-lifecycle": { "version": "3.1.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-lifecycle/-/npm-lifecycle-3.1.5.tgz", + "integrity": "sha512-lDLVkjfZmvmfvpvBzA4vzee9cn+Me4orq0QF8glbswJVEbIcSNWib7qGOffolysc3teCqbbPZZkzbr3GQZTL1g==", "dev": true, "requires": { "byline": "^5.0.0", @@ -12383,17 +12623,20 @@ }, "npm-logical-tree": { "version": "1.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-logical-tree/-/npm-logical-tree-1.2.1.tgz", + "integrity": "sha512-AJI/qxDB2PWI4LG1CYN579AY1vCiNyWfkiquCsJWqntRu/WwimVrC8yXeILBFHDwxfOejxewlmnvW9XXjMlYIg==", "dev": true }, "npm-normalize-package-bin": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", "dev": true }, "npm-package-arg": { "version": "6.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz", + "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", "dev": true, "requires": { "hosted-git-info": "^2.7.1", @@ -12404,7 +12647,8 @@ }, "npm-packlist": { "version": "1.4.8", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", + "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", "dev": true, "requires": { "ignore-walk": "^3.0.1", @@ -12414,7 +12658,8 @@ }, "npm-pick-manifest": { "version": "3.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-3.0.2.tgz", + "integrity": "sha512-wNprTNg+X5nf+tDi+hbjdHhM4bX+mKqv6XmPh7B5eG+QY9VARfQPfCEH013H5GqfNj6ee8Ij2fg8yk0mzps1Vw==", "dev": true, "requires": { "figgy-pudding": "^3.5.1", @@ -12424,7 +12669,8 @@ }, "npm-profile": { "version": "4.0.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-profile/-/npm-profile-4.0.4.tgz", + "integrity": "sha512-Ta8xq8TLMpqssF0H60BXS1A90iMoM6GeKwsmravJ6wYjWwSzcYBTdyWa3DZCYqPutacBMEm7cxiOkiIeCUAHDQ==", "dev": true, "requires": { "aproba": "^1.1.2 || 2", @@ -12434,7 +12680,8 @@ }, "npm-registry-fetch": { "version": "4.0.7", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.7.tgz", + "integrity": "sha512-cny9v0+Mq6Tjz+e0erFAB+RYJ/AVGzkjnISiobqP8OWj9c9FLoZZu8/SPSKJWE17F1tk4018wfjV+ZbIbqC7fQ==", "dev": true, "requires": { "JSONStream": "^1.3.4", @@ -12448,14 +12695,16 @@ "dependencies": { "safe-buffer": { "version": "5.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true } } }, "npm-run-path": { "version": "2.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, "requires": { "path-key": "^2.0.0" @@ -12463,12 +12712,14 @@ }, "npm-user-validate": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-user-validate/-/npm-user-validate-1.0.1.tgz", + "integrity": "sha512-uQwcd/tY+h1jnEaze6cdX/LrhWhoBxfSknxentoqmIuStxUExxjWd3ULMLFPiFUrZKbOVMowH6Jq2FRWfmhcEw==", "dev": true }, "npmlog": { "version": "4.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "requires": { "are-we-there-yet": "~1.1.2", @@ -12479,27 +12730,32 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, "oauth-sign": { "version": "0.9.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", "dev": true }, "object-assign": { "version": "4.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, "object-keys": { "version": "1.0.12", - "bundled": true, + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", + "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==", "dev": true }, "object.getownpropertydescriptors": { "version": "2.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", "dev": true, "requires": { "define-properties": "^1.1.2", @@ -12508,7 +12764,8 @@ }, "once": { "version": "1.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { "wrappy": "1" @@ -12516,22 +12773,26 @@ }, "opener": { "version": "1.5.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", "dev": true }, "os-homedir": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, "os-tmpdir": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, "osenv": { "version": "0.1.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "requires": { "os-homedir": "^1.0.0", @@ -12540,12 +12801,14 @@ }, "p-finally": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", "dev": true }, "package-json": { "version": "4.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", + "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", "dev": true, "requires": { "got": "^6.7.1", @@ -12556,7 +12819,8 @@ }, "pacote": { "version": "9.5.12", - "bundled": true, + "resolved": "https://registry.npmjs.org/pacote/-/pacote-9.5.12.tgz", + "integrity": "sha512-BUIj/4kKbwWg4RtnBncXPJd15piFSVNpTzY0rysSr3VnMowTYgkGKcaHrbReepAkjTr8lH2CVWRi58Spg2CicQ==", "dev": true, "requires": { "bluebird": "^3.5.3", @@ -12593,7 +12857,8 @@ "dependencies": { "minipass": { "version": "2.9.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", "dev": true, "requires": { "safe-buffer": "^5.1.2", @@ -12604,7 +12869,8 @@ }, "parallel-transform": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", + "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", "dev": true, "requires": { "cyclist": "~0.2.2", @@ -12614,7 +12880,8 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -12628,7 +12895,8 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -12638,57 +12906,68 @@ }, "path-exists": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", "dev": true }, "path-is-absolute": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, "path-is-inside": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", "dev": true }, "path-key": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", "dev": true }, "path-parse": { "version": "1.0.7", - "bundled": true, + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "performance-now": { "version": "2.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, "pify": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true }, "prepend-http": { "version": "1.0.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", "dev": true }, "process-nextick-args": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true }, "promise-inflight": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", "dev": true }, "promise-retry": { "version": "1.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz", + "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=", "dev": true, "requires": { "err-code": "^1.0.0", @@ -12697,14 +12976,16 @@ "dependencies": { "retry": { "version": "0.10.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", + "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=", "dev": true } } }, "promzard": { "version": "0.3.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/promzard/-/promzard-0.3.0.tgz", + "integrity": "sha1-JqXW7ox97kyxIggwWs+5O6OCqe4=", "dev": true, "requires": { "read": "1" @@ -12712,12 +12993,14 @@ }, "proto-list": { "version": "1.2.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", "dev": true }, "protoduck": { "version": "5.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.1.tgz", + "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==", "dev": true, "requires": { "genfun": "^5.0.0" @@ -12725,22 +13008,26 @@ }, "prr": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", "dev": true }, "pseudomap": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, "psl": { "version": "1.1.29", - "bundled": true, + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", + "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==", "dev": true }, "pump": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -12749,7 +13036,8 @@ }, "pumpify": { "version": "1.5.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", "dev": true, "requires": { "duplexify": "^3.6.0", @@ -12759,7 +13047,8 @@ "dependencies": { "pump": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -12770,22 +13059,26 @@ }, "punycode": { "version": "1.4.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", "dev": true }, "qrcode-terminal": { "version": "0.12.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz", + "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==", "dev": true }, "qs": { "version": "6.5.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", "dev": true }, "query-string": { "version": "6.8.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.8.2.tgz", + "integrity": "sha512-J3Qi8XZJXh93t2FiKyd/7Ec6GNifsjKXUsVFkSBj/kjLsDylWhnCz4NT1bkPcKotttPW+QbKGqqPH8OoI2pdqw==", "dev": true, "requires": { "decode-uri-component": "^0.2.0", @@ -12795,12 +13088,14 @@ }, "qw": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/qw/-/qw-1.0.1.tgz", + "integrity": "sha1-77/cdA+a0FQwRCassYNBLMi5ltQ=", "dev": true }, "rc": { "version": "1.2.8", - "bundled": true, + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "requires": { "deep-extend": "^0.6.0", @@ -12811,7 +13106,8 @@ }, "read": { "version": "1.0.7", - "bundled": true, + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", "dev": true, "requires": { "mute-stream": "~0.0.4" @@ -12819,7 +13115,8 @@ }, "read-cmd-shim": { "version": "1.0.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-1.0.5.tgz", + "integrity": "sha512-v5yCqQ/7okKoZZkBQUAfTsQ3sVJtXdNfbPnI5cceppoxEVLYA3k+VtV2omkeo8MS94JCy4fSiUwlRBAwCVRPUA==", "dev": true, "requires": { "graceful-fs": "^4.1.2" @@ -12827,7 +13124,8 @@ }, "read-installed": { "version": "4.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/read-installed/-/read-installed-4.0.3.tgz", + "integrity": "sha1-/5uLZ/GH0eTCm5/rMfayI6zRkGc=", "dev": true, "requires": { "debuglog": "^1.0.1", @@ -12841,7 +13139,8 @@ }, "read-package-json": { "version": "2.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.1.tgz", + "integrity": "sha512-dAiqGtVc/q5doFz6096CcnXhpYk0ZN8dEKVkGLU0CsASt8SrgF6SF7OTKAYubfvFhWaqofl+Y8HK19GR8jwW+A==", "dev": true, "requires": { "glob": "^7.1.1", @@ -12853,7 +13152,8 @@ }, "read-package-tree": { "version": "5.3.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.3.1.tgz", + "integrity": "sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==", "dev": true, "requires": { "read-package-json": "^2.0.0", @@ -12863,7 +13163,8 @@ }, "readable-stream": { "version": "3.6.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "dev": true, "requires": { "inherits": "^2.0.3", @@ -12873,7 +13174,8 @@ }, "readdir-scoped-modules": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", + "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", "dev": true, "requires": { "debuglog": "^1.0.1", @@ -12884,7 +13186,8 @@ }, "registry-auth-token": { "version": "3.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", + "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", "dev": true, "requires": { "rc": "^1.1.6", @@ -12893,7 +13196,8 @@ }, "registry-url": { "version": "3.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", "dev": true, "requires": { "rc": "^1.0.1" @@ -12901,7 +13205,8 @@ }, "request": { "version": "2.88.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", "dev": true, "requires": { "aws-sign2": "~0.7.0", @@ -12928,27 +13233,32 @@ }, "require-directory": { "version": "2.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, "require-main-filename": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, "resolve-from": { "version": "4.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, "retry": { "version": "0.12.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", "dev": true }, "rimraf": { "version": "2.7.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dev": true, "requires": { "glob": "^7.1.3" @@ -12956,7 +13266,8 @@ }, "run-queue": { "version": "1.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", "dev": true, "requires": { "aproba": "^1.1.1" @@ -12964,29 +13275,34 @@ "dependencies": { "aproba": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true } } }, "safe-buffer": { "version": "5.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, "safer-buffer": { "version": "2.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, "semver": { "version": "5.7.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, "semver-diff": { "version": "2.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", + "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", "dev": true, "requires": { "semver": "^5.0.3" @@ -12994,12 +13310,14 @@ }, "set-blocking": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, "sha": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/sha/-/sha-3.0.0.tgz", + "integrity": "sha512-DOYnM37cNsLNSGIG/zZWch5CKIRNoLdYUQTQlcgkRkoYIUwDYjqDyye16YcDZg/OPdcbUgTKMjc4SY6TB7ZAPw==", "dev": true, "requires": { "graceful-fs": "^4.1.2" @@ -13007,7 +13325,8 @@ }, "shebang-command": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { "shebang-regex": "^1.0.0" @@ -13015,27 +13334,32 @@ }, "shebang-regex": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, "signal-exit": { "version": "3.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, "slide": { "version": "1.1.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", "dev": true }, "smart-buffer": { "version": "4.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", + "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==", "dev": true }, "socks": { "version": "2.3.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.3.tgz", + "integrity": "sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA==", "dev": true, "requires": { "ip": "1.1.5", @@ -13044,7 +13368,8 @@ }, "socks-proxy-agent": { "version": "4.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz", + "integrity": "sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg==", "dev": true, "requires": { "agent-base": "~4.2.1", @@ -13053,7 +13378,8 @@ "dependencies": { "agent-base": { "version": "4.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", + "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", "dev": true, "requires": { "es6-promisify": "^5.0.0" @@ -13063,12 +13389,14 @@ }, "sorted-object": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/sorted-object/-/sorted-object-2.0.1.tgz", + "integrity": "sha1-fWMfS9OnmKJK8d/8+/6DM3pd9fw=", "dev": true }, "sorted-union-stream": { "version": "2.1.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/sorted-union-stream/-/sorted-union-stream-2.1.3.tgz", + "integrity": "sha1-x3lMfgd4gAUv9xqNSi27Sppjisc=", "dev": true, "requires": { "from2": "^1.3.0", @@ -13077,7 +13405,8 @@ "dependencies": { "from2": { "version": "1.3.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/from2/-/from2-1.3.0.tgz", + "integrity": "sha1-iEE7qqX5pZfP3pIh2GmGzTwGHf0=", "dev": true, "requires": { "inherits": "~2.0.1", @@ -13086,12 +13415,14 @@ }, "isarray": { "version": "0.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", "dev": true }, "readable-stream": { "version": "1.1.14", - "bundled": true, + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -13102,14 +13433,16 @@ }, "string_decoder": { "version": "0.10.31", - "bundled": true, + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", "dev": true } } }, "spdx-correct": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", + "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", @@ -13118,12 +13451,14 @@ }, "spdx-exceptions": { "version": "2.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", + "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==", "dev": true }, "spdx-expression-parse": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", "dev": true, "requires": { "spdx-exceptions": "^2.1.0", @@ -13132,17 +13467,20 @@ }, "spdx-license-ids": { "version": "3.0.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", "dev": true }, "split-on-first": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", "dev": true }, "sshpk": { "version": "1.14.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", + "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", "dev": true, "requires": { "asn1": "~0.2.3", @@ -13158,7 +13496,8 @@ }, "ssri": { "version": "6.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", + "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", "dev": true, "requires": { "figgy-pudding": "^3.5.1" @@ -13166,7 +13505,8 @@ }, "stream-each": { "version": "1.2.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.2.tgz", + "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==", "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -13175,7 +13515,8 @@ }, "stream-iterate": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/stream-iterate/-/stream-iterate-1.2.0.tgz", + "integrity": "sha1-K9fHcpbBcCpGSIuK1B95hl7s1OE=", "dev": true, "requires": { "readable-stream": "^2.1.5", @@ -13184,7 +13525,8 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -13198,7 +13540,8 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -13208,17 +13551,20 @@ }, "stream-shift": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", "dev": true }, "strict-uri-encode": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=", "dev": true }, "string-width": { "version": "2.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { "is-fullwidth-code-point": "^2.0.0", @@ -13227,17 +13573,20 @@ "dependencies": { "ansi-regex": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, "strip-ansi": { "version": "4.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { "ansi-regex": "^3.0.0" @@ -13247,7 +13596,8 @@ }, "string_decoder": { "version": "1.3.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "requires": { "safe-buffer": "~5.2.0" @@ -13255,19 +13605,22 @@ "dependencies": { "safe-buffer": { "version": "5.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", "dev": true } } }, "stringify-package": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.1.tgz", + "integrity": "sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg==", "dev": true }, "strip-ansi": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { "ansi-regex": "^2.0.0" @@ -13275,17 +13628,20 @@ }, "strip-eof": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, "strip-json-comments": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true }, "supports-color": { "version": "5.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -13293,7 +13649,8 @@ }, "tar": { "version": "4.4.19", - "bundled": true, + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", + "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", "dev": true, "requires": { "chownr": "^1.1.4", @@ -13307,7 +13664,8 @@ "dependencies": { "minipass": { "version": "2.9.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", "dev": true, "requires": { "safe-buffer": "^5.1.2", @@ -13316,19 +13674,22 @@ }, "safe-buffer": { "version": "5.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, "yallist": { "version": "3.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true } } }, "term-size": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", + "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", "dev": true, "requires": { "execa": "^0.7.0" @@ -13336,17 +13697,20 @@ }, "text-table": { "version": "0.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, "through": { "version": "2.3.8", - "bundled": true, + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, "through2": { "version": "2.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", "dev": true, "requires": { "readable-stream": "^2.1.5", @@ -13355,7 +13719,8 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -13369,7 +13734,8 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -13379,17 +13745,20 @@ }, "timed-out": { "version": "4.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", "dev": true }, "tiny-relative-date": { "version": "1.3.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/tiny-relative-date/-/tiny-relative-date-1.3.0.tgz", + "integrity": "sha512-MOQHpzllWxDCHHaDno30hhLfbouoYlOI8YlMNtvKe1zXbjEVhbcEovQxvZrPvtiYW630GQDoMMarCnjfyfHA+A==", "dev": true }, "tough-cookie": { "version": "2.4.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "dev": true, "requires": { "psl": "^1.1.24", @@ -13398,7 +13767,8 @@ }, "tunnel-agent": { "version": "0.6.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "dev": true, "requires": { "safe-buffer": "^5.0.1" @@ -13406,28 +13776,33 @@ }, "tweetnacl": { "version": "0.14.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "dev": true, "optional": true }, "typedarray": { "version": "0.0.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, "uid-number": { "version": "0.0.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", + "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=", "dev": true }, "umask": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/umask/-/umask-1.1.0.tgz", + "integrity": "sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0=", "dev": true }, "unique-filename": { "version": "1.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", "dev": true, "requires": { "unique-slug": "^2.0.0" @@ -13435,7 +13810,8 @@ }, "unique-slug": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz", + "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=", "dev": true, "requires": { "imurmurhash": "^0.1.4" @@ -13443,7 +13819,8 @@ }, "unique-string": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", + "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", "dev": true, "requires": { "crypto-random-string": "^1.0.0" @@ -13451,17 +13828,20 @@ }, "unpipe": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", "dev": true }, "unzip-response": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", + "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=", "dev": true }, "update-notifier": { "version": "2.5.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", + "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", "dev": true, "requires": { "boxen": "^1.2.1", @@ -13478,7 +13858,8 @@ }, "uri-js": { "version": "4.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", + "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", "dev": true, "requires": { "punycode": "^2.1.0" @@ -13486,14 +13867,16 @@ "dependencies": { "punycode": { "version": "2.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true } } }, "url-parse-lax": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", "dev": true, "requires": { "prepend-http": "^1.0.1" @@ -13501,17 +13884,20 @@ }, "util-deprecate": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, "util-extend": { "version": "1.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz", + "integrity": "sha1-p8IW0mdUUWljeztu3GypEZ4v+T8=", "dev": true }, "util-promisify": { "version": "2.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/util-promisify/-/util-promisify-2.1.0.tgz", + "integrity": "sha1-PCI2R2xNMsX/PEcAKt18E7moKlM=", "dev": true, "requires": { "object.getownpropertydescriptors": "^2.0.3" @@ -13519,12 +13905,14 @@ }, "uuid": { "version": "3.3.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", "dev": true }, "validate-npm-package-license": { "version": "3.0.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, "requires": { "spdx-correct": "^3.0.0", @@ -13533,7 +13921,8 @@ }, "validate-npm-package-name": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", "dev": true, "requires": { "builtins": "^1.0.3" @@ -13541,7 +13930,8 @@ }, "verror": { "version": "1.10.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "dev": true, "requires": { "assert-plus": "^1.0.0", @@ -13551,7 +13941,8 @@ }, "wcwidth": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", "dev": true, "requires": { "defaults": "^1.0.3" @@ -13559,7 +13950,8 @@ }, "which": { "version": "1.3.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -13567,12 +13959,14 @@ }, "which-module": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, "wide-align": { "version": "1.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", + "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", "dev": true, "requires": { "string-width": "^1.0.2" @@ -13580,7 +13974,8 @@ "dependencies": { "string-width": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { "code-point-at": "^1.0.0", @@ -13592,7 +13987,8 @@ }, "widest-line": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", + "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", "dev": true, "requires": { "string-width": "^2.1.1" @@ -13600,7 +13996,8 @@ }, "worker-farm": { "version": "1.7.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", "dev": true, "requires": { "errno": "~0.1.7" @@ -13608,7 +14005,8 @@ }, "wrap-ansi": { "version": "5.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", "dev": true, "requires": { "ansi-styles": "^3.2.0", @@ -13618,17 +14016,20 @@ "dependencies": { "ansi-regex": { "version": "4.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, "string-width": { "version": "3.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { "emoji-regex": "^7.0.1", @@ -13638,7 +14039,8 @@ }, "strip-ansi": { "version": "5.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { "ansi-regex": "^4.1.0" @@ -13648,12 +14050,14 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, "write-file-atomic": { "version": "2.4.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", "dev": true, "requires": { "graceful-fs": "^4.1.11", @@ -13663,27 +14067,32 @@ }, "xdg-basedir": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", + "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", "dev": true }, "xtend": { "version": "4.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", "dev": true }, "y18n": { "version": "4.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", "dev": true }, "yallist": { "version": "3.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", "dev": true }, "yargs": { "version": "14.2.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz", + "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==", "dev": true, "requires": { "cliui": "^5.0.0", @@ -13701,12 +14110,14 @@ "dependencies": { "ansi-regex": { "version": "4.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, "find-up": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { "locate-path": "^3.0.0" @@ -13714,12 +14125,14 @@ }, "is-fullwidth-code-point": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, "locate-path": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { "p-locate": "^3.0.0", @@ -13728,7 +14141,8 @@ }, "p-limit": { "version": "2.3.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -13736,7 +14150,8 @@ }, "p-locate": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { "p-limit": "^2.0.0" @@ -13744,12 +14159,14 @@ }, "p-try": { "version": "2.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, "string-width": { "version": "3.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { "emoji-regex": "^7.0.1", @@ -13759,7 +14176,8 @@ }, "strip-ansi": { "version": "5.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { "ansi-regex": "^4.1.0" @@ -13769,7 +14187,8 @@ }, "yargs-parser": { "version": "15.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz", + "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -13778,7 +14197,8 @@ "dependencies": { "camelcase": { "version": "5.3.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true } } @@ -14340,7 +14760,7 @@ "pushdata-bitcoin": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/pushdata-bitcoin/-/pushdata-bitcoin-1.0.1.tgz", - "integrity": "sha1-FZMdPNlnreUiBvUjqnMxrvfUOvc=", + "integrity": "sha512-hw7rcYTJRAl4olM8Owe8x0fBuJJ+WGbMhQuLWOXEMN3PxPCKQHRkhfL+XG0+iXUmSHjkMmb3Ba55Mt21cZc9kQ==", "requires": { "bitcoin-ops": "^1.3.0" } @@ -14812,9 +15232,9 @@ } }, "safe-stable-stringify": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz", - "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==" + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.2.tgz", + "integrity": "sha512-gMxvPJYhP0O9n2pvcfYfIuYgbledAOJFcqRThtPRmjscaipiwcwPPKLytpVzMkG2HAN87Qmo2d4PtGiri1dSLA==" }, "safer-buffer": { "version": "2.1.2", @@ -15095,7 +15515,7 @@ "simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", "requires": { "is-arrayish": "^0.3.1" } @@ -15412,7 +15832,7 @@ "stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==" }, "stack-utils": { "version": "1.0.5", @@ -16029,7 +16449,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "util.promisify": { "version": "1.1.1", @@ -16199,35 +16619,37 @@ "wif": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz", - "integrity": "sha1-CNP1IFbGZnkplyb63g1DKudLRwQ=", + "integrity": "sha512-HIanZn1zmduSF+BQhkE+YXIbEiH0xPr1012QbFEGB0xsKqJii0/SqJjyn8dFv6y36kOznMgMB+LGcbZTJ1xACQ==", "requires": { "bs58check": "<3.0.0" } }, "winston": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.4.0.tgz", - "integrity": "sha512-FqilVj+5HKwCfIHQzMxrrd5tBIH10JTS3koFGbLVWBODjiIYq7zir08rFyBT4rrTYG/eaTqDcfSIbcjSM78YSw==", + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.8.2.tgz", + "integrity": "sha512-MsE1gRx1m5jdTTO9Ld/vND4krP2To+lgDoMEHGGa4HIlAUyXJtfc7CxQcGXVyz2IBpw5hbFkj2b/AtUdQwyRew==", "requires": { + "@colors/colors": "1.5.0", "@dabh/diagnostics": "^2.0.2", "async": "^3.2.3", "is-stream": "^2.0.0", - "logform": "^2.3.2", + "logform": "^2.4.0", "one-time": "^1.0.0", "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", "stack-trace": "0.0.x", "triple-beam": "^1.3.0", - "winston-transport": "^4.4.2" + "winston-transport": "^4.5.0" } }, "winston-transport": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.2.tgz", - "integrity": "sha512-9jmhltAr5ygt5usgUTQbEiw/7RYXpyUbEAFRCSicIacpUzPkrnQsQZSPGEI12aLK9Jth4zNcYJx3Cvznwrl8pw==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz", + "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==", "requires": { "logform": "^2.3.2", - "readable-stream": "^3.4.0", - "triple-beam": "^1.2.0" + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" } }, "word-wrap": { diff --git a/package.json b/package.json index 2ee25718..d158b089 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@identity.com/credential-commons", - "version": "3.0.0", + "version": "3.0.1", "author": "Identity.com Community", "license": "MIT", "description": "Verifiable Credential and Attestation Library", @@ -69,7 +69,7 @@ "@identity.com/did-io-driver-sol": "^1.0.0-beta.0", "@identity.com/uca": "github:identity-com/uca#v1.0.30", "ajv": "^7.2.4", - "ajv-formats": "^2.0.2", + "ajv-formats": "^1.6.1", "babel-runtime": "^6.26.0", "bitcoinjs-lib": "https://github.com/dabura667/bitcoinjs-lib.git#bcash330", "bottlejs": "^1.7.1", @@ -90,8 +90,7 @@ "type-of-is": "^3.5.1", "unix-timestamp": "^0.2.0", "uuid": "^3.3.2", - "valid-url": "^1.0.9", - "winston": "^3.3.3" + "valid-url": "^1.0.9" }, "jest": { "modulePathIgnorePatterns": [ diff --git a/src/lib/signerVerifier.js b/src/lib/signerVerifier.js index be26cf17..aeb6329e 100644 --- a/src/lib/signerVerifier.js +++ b/src/lib/signerVerifier.js @@ -1,10 +1,7 @@ const nacl = require('tweetnacl'); const bs58 = require('bs58'); -const { TextEncoder } = require('util'); const didUtil = require('./did'); -const textEncoder = new TextEncoder(); - class Ed25519Signer { constructor(key, verificationMethod) { this.key = key; @@ -12,7 +9,7 @@ class Ed25519Signer { } sign(proof) { - const signed = nacl.sign.detached(textEncoder.encode(proof.merkleRoot), bs58.decode(this.key)); + const signed = nacl.sign.detached(Buffer.from(proof.merkleRoot, 'hex'), bs58.decode(this.key)); const signature = Buffer.from(signed).toString('hex'); return { @@ -29,7 +26,7 @@ class Ed25519Verifier { verify(vc) { return nacl.sign.detached.verify( - textEncoder.encode(vc.proof.merkleRoot), + Buffer.from(vc.proof.merkleRoot, 'hex'), Uint8Array.from(Buffer.from(vc.proof.merkleRootSignature.signature, 'hex')), bs58.decode(this.key), ); diff --git a/src/logger.js b/src/logger.js index 2d057819..63e3cd20 100644 --- a/src/logger.js +++ b/src/logger.js @@ -1,16 +1,22 @@ -const { createLogger, format, transports } = require('winston'); - -// Configure the Winston logger. For the complete documentation see https://github.com/winstonjs/winston -const logger = createLogger({ - // To see more detailed errors, change this to 'debug' - level: 'info', - format: format.combine( - format.splat(), - format.simple(), - ), - transports: [ - new transports.Console(), - ], -}); - -module.exports = logger; +module.exports = { + // eslint-disable-next-line no-unused-vars + warn(a, b = undefined) { + // eslint-disable-next-line no-console + console.warn(a); + }, + // eslint-disable-next-line no-unused-vars + info(a, b = undefined) { + // eslint-disable-next-line no-console + console.info(a); + }, + // eslint-disable-next-line no-unused-vars + debug(a, b = undefined) { + // eslint-disable-next-line no-console + // console.debug(a); // disabled + }, + // eslint-disable-next-line no-unused-vars + error(a, b = undefined) { + // eslint-disable-next-line no-console + console.error(a); + }, +};