cassproject 5.0.10 → 5.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -46,10 +46,10 @@ Development unit tests presume you have a CaSS Repository running on `localhost:
46
46
  ## Publish checklist
47
47
 
48
48
  * `npm upgrade --save` Review dependencies, autocomplete version numbers to latest versions.
49
- * Increment version number using `npm version <patch|minor|major>`. This automatically updates `package.json` and `yuidoc.json`.
49
+ * Increment version number using `npm version --no-git-tag-version <patch|minor|major>`. This automatically updates `package.json` and `yuidoc.json`.
50
50
  * Update changelog using `npm run changelog`, and review the changes in `CHANGELOG.md`.
51
51
  * `npm install`
52
- * `npm audit` and fix any audit issues.
52
+ * `npm audit` and fix any audit issues. Stop if `npm audit --omit=dev` has findings.
53
53
  * Update CaSS server version if necessary in package.json
54
54
  * `npm test` - Must not fail any tests.
55
55
  * `npm run webpack:cypressFirefoxHttps` See if the firefox test case has changed.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cassproject",
3
- "version": "5.0.10",
3
+ "version": "5.0.12",
4
4
  "description": "Competency and Skills Service",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -10,15 +10,15 @@
10
10
  "multitest": "concurrently --kill-others-on-fail \"npm run test15\" \"npm run test14\" \"npm run test13\" \"npm run test12\"",
11
11
  "testCassTest": "npm run testkill && docker run -d --platform linux/amd64 --network cass-net --name cass-test -p80:80 -e CASS_LOOPBACK cass-test && wait-on http://localhost/api/ping && npm run testNode18 && npm run testNode18Fips && npm run testCypressEdge && npm run testCypress && npm run testkill",
12
12
  "testDevHttps": "npm run testkill && docker run -d --platform linux/amd64 --network cass-net --name cass-test -p443:80 -e CASS_LOOPBACK -e HTTPS=true cassproject/cass:dev && wait-on https://localhost/api/ping && npm run testNode18HttpsFips && npm run testNode18Https && npm run testCypressEdgeHttps && npm run testCypressHttps",
13
- "test16HttpsFips": "export CASS_LOOPBACK=https://cass-testsf16/api/|| set CASS_LOOPBACK=https://cass-testsf16/api/&& npm run testkillsf16 && docker run -d --platform linux/amd64 --network cass-net --name cass-testsf16 -p450:443 -e CASS_LOOPBACK -e HTTPS=true cassproject/cass:1.6.8 && wait-on https://localhost:450/api/ping && concurrently --kill-others-on-fail \"npm run testCypressHttps\" \"npm run testNode24Https\" \"npm run testNode24HttpsFips\" \"npm run testNode24HttpsForceFips\" \"npm run testNode22Https\" \"npm run testNode22HttpsFips\" \"npm run testNode22HttpsForceFips\" \"npm run testNode20Https\" \"npm run testNode20HttpsFips\" \"npm run testNode20HttpsForceFips\" \"npm run testNode18Https\" && npm run testkillsf16",
14
- "test16Https11Fips": "export CASS_LOOPBACK=https://cass-testsf1116/api/|| set CASS_LOOPBACK=https://cass-testsf1116/api/&& npm run testkillsf1116 && docker run -d --platform linux/amd64 --network cass-net --name cass-testsf1116 -p449:443 -e CASS_LOOPBACK -e HTTPS=true -e HTTP2=false cassproject/cass:1.6.8 && wait-on https://localhost:449/api/ping && concurrently --kill-others-on-fail \"npm run testCypressHttps\" \"npm run testNode24Https\" \"npm run testNode24HttpsFips\" \"npm run testNode24HttpsForceFips\" \"npm run testNode22Https\" \"npm run testNode22HttpsFips\" \"npm run testNode22HttpsForceFips\" \"npm run testNode20Https\" \"npm run testNode20HttpsFips\" \"npm run testNode20HttpsForceFips\" \"npm run testNode18Https\" && npm run testkillsf1116",
15
- "test16Https": "export CASS_LOOPBACK=https://cass-tests16/api/|| set CASS_LOOPBACK=https://cass-tests16/api/&& npm run testkills16 && docker run -d --platform linux/amd64 --network cass-net --name cass-tests16 -p448:443 -e CASS_LOOPBACK -e HTTPS=true cassproject/cass:1.6.8 && wait-on https://localhost:448/api/ping && concurrently --kill-others-on-fail \"npm run testCypressHttps\" \"npm run testNode24Https\" \"npm run testNode24HttpsFips\" \"npm run testNode22Https\" \"npm run testNode22HttpsFips\" \"npm run testNode20Https\" \"npm run testNode20HttpsFips\" \"npm run testNode18Https\" && npm run testkills16",
16
- "test16HttpsNoFips": "export CASS_LOOPBACK=https://cass-tests16/api/|| set CASS_LOOPBACK=https://cass-tests16/api/&& npm run testkills16 && docker run -d --platform linux/amd64 --network cass-net --name cass-tests16 -p448:443 -e CASS_LOOPBACK -e HTTPS=true cassproject/cass:1.6.8 && wait-on https://localhost:448/api/ping && concurrently --kill-others-on-fail \"npm run testCypressHttps\" \"npm run testNode24Https\" \"npm run testNode22Https\" \"npm run testNode20Https\" \"npm run testNode18Https\" && npm run testkills16",
17
- "test16Https11": "export CASS_LOOPBACK=https://cass-tests1116/api/|| set CASS_LOOPBACK=https://cass-tests1116/api/&& npm run testkills1116 && docker run -d --platform linux/amd64 --network cass-net --name cass-tests1116 -p451:443 -e CASS_LOOPBACK -e HTTPS=true -e HTTP2=false cassproject/cass:1.6.8 && wait-on https://localhost:451/api/ping && concurrently --kill-others-on-fail \"npm run testCypressHttps\" \"npm run testNode24Https\" \"npm run testNode24HttpsFips\" \"npm run testNode22Https\" \"npm run testNode22HttpsFips\" \"npm run testNode20Https\" \"npm run testNode20HttpsFips\" \"npm run testNode18Https\" && npm run testkills1116",
18
- "test16Https11NoFips": "export CASS_LOOPBACK=https://cass-tests1116/api/|| set CASS_LOOPBACK=https://cass-tests1116/api/&& npm run testkills1116 && docker run -d --platform linux/amd64 --network cass-net --name cass-tests1116 -p451:443 -e CASS_LOOPBACK -e HTTPS=true -e HTTP2=false cassproject/cass:1.6.8 && wait-on https://localhost:451/api/ping && concurrently --kill-others-on-fail \"npm run testCypressHttps\" \"npm run testNode24Https\" \"npm run testNode22Https\" \"npm run testNode20Https\" \"npm run testNode18Https\" && npm run testkills1116",
19
- "test16Fips": "export CASS_LOOPBACK=http://cass-testf16/api/|| set CASS_LOOPBACK=http://cass-testf16/api/&& npm run testkillf16 && docker run -d --platform linux/amd64 --network cass-net -e CASS_LOOPBACK --name cass-testf16 -p83:80 cassproject/cass:1.6.8 && wait-on http://localhost:83/api/ping && concurrently --kill-others-on-fail \"npm run testCypress\" \"npm run testNode24\" \"npm run testNode24Fips\" \"npm run testNode24ForceFips\" \"npm run testNode22\" \"npm run testNode22Fips\" \"npm run testNode22ForceFips\" \"npm run testNode20\" \"npm run testNode20Fips\" \"npm run testNode20ForceFips\" \"npm run testNode18\" && npm run testkillf16",
20
- "test16": "export CASS_LOOPBACK=http://cass-test16/api/|| set CASS_LOOPBACK=http://cass-test16/api/&& npm run testkill16 && docker run -d --platform linux/amd64 --network cass-net -e CASS_LOOPBACK --name cass-test16 -p82:80 cassproject/cass:1.6.8 && wait-on http://localhost:82/api/ping && concurrently --kill-others-on-fail \"npm run testCypress\" \"npm run testNode24\" \"npm run testNode24Fips\" \"npm run testNode22\" \"npm run testNode22Fips\" \"npm run testNode20\" \"npm run testNode20Fips\" \"npm run testNode18\" && npm run testkill16",
21
- "test16NoFips": "export CASS_LOOPBACK=http://cass-test16/api/|| set CASS_LOOPBACK=http://cass-test16/api/&& npm run testkill16 && docker run -d --platform linux/amd64 --network cass-net -e CASS_LOOPBACK --name cass-test16 -p82:80 cassproject/cass:1.6.8 && wait-on http://localhost:82/api/ping && concurrently --kill-others-on-fail \"npm run testCypress\" \"npm run testNode24\" \"npm run testNode22\" \"npm run testNode20\" \"npm run testNode18\" && npm run testkill16",
13
+ "test16HttpsFips": "export CASS_LOOPBACK=https://cass-testsf16/api/|| set CASS_LOOPBACK=https://cass-testsf16/api/&& npm run testkillsf16 && docker run -d --platform linux/amd64 --network cass-net --name cass-testsf16 -p450:443 -e CASS_LOOPBACK -e HTTPS=true cassproject/cass:1.6 && wait-on https://localhost:450/api/ping && concurrently --kill-others-on-fail \"npm run testCypressHttps\" \"npm run testNode24Https\" \"npm run testNode24HttpsFips\" \"npm run testNode24HttpsForceFips\" \"npm run testNode22Https\" \"npm run testNode22HttpsFips\" \"npm run testNode22HttpsForceFips\" \"npm run testNode20Https\" \"npm run testNode20HttpsFips\" \"npm run testNode20HttpsForceFips\" \"npm run testNode18Https\" && npm run testkillsf16",
14
+ "test16Https11Fips": "export CASS_LOOPBACK=https://cass-testsf1116/api/|| set CASS_LOOPBACK=https://cass-testsf1116/api/&& npm run testkillsf1116 && docker run -d --platform linux/amd64 --network cass-net --name cass-testsf1116 -p449:443 -e CASS_LOOPBACK -e HTTPS=true -e HTTP2=false cassproject/cass:1.6 && wait-on https://localhost:449/api/ping && concurrently --kill-others-on-fail \"npm run testCypressHttps\" \"npm run testNode24Https\" \"npm run testNode24HttpsFips\" \"npm run testNode24HttpsForceFips\" \"npm run testNode22Https\" \"npm run testNode22HttpsFips\" \"npm run testNode22HttpsForceFips\" \"npm run testNode20Https\" \"npm run testNode20HttpsFips\" \"npm run testNode20HttpsForceFips\" \"npm run testNode18Https\" && npm run testkillsf1116",
15
+ "test16Https": "export CASS_LOOPBACK=https://cass-tests16/api/|| set CASS_LOOPBACK=https://cass-tests16/api/&& npm run testkills16 && docker run -d --platform linux/amd64 --network cass-net --name cass-tests16 -p448:443 -e CASS_LOOPBACK -e HTTPS=true cassproject/cass:1.6 && wait-on https://localhost:448/api/ping && concurrently --kill-others-on-fail \"npm run testCypressHttps\" \"npm run testNode24Https\" \"npm run testNode24HttpsFips\" \"npm run testNode22Https\" \"npm run testNode22HttpsFips\" \"npm run testNode20Https\" \"npm run testNode20HttpsFips\" \"npm run testNode18Https\" && npm run testkills16",
16
+ "test16HttpsNoFips": "export CASS_LOOPBACK=https://cass-tests16/api/|| set CASS_LOOPBACK=https://cass-tests16/api/&& npm run testkills16 && docker run -d --platform linux/amd64 --network cass-net --name cass-tests16 -p448:443 -e CASS_LOOPBACK -e HTTPS=true cassproject/cass:1.6 && wait-on https://localhost:448/api/ping && concurrently --kill-others-on-fail \"npm run testCypressHttps\" \"npm run testNode24Https\" \"npm run testNode22Https\" \"npm run testNode20Https\" \"npm run testNode18Https\" && npm run testkills16",
17
+ "test16Https11": "export CASS_LOOPBACK=https://cass-tests1116/api/|| set CASS_LOOPBACK=https://cass-tests1116/api/&& npm run testkills1116 && docker run -d --platform linux/amd64 --network cass-net --name cass-tests1116 -p451:443 -e CASS_LOOPBACK -e HTTPS=true -e HTTP2=false cassproject/cass:1.6 && wait-on https://localhost:451/api/ping && concurrently --kill-others-on-fail \"npm run testCypressHttps\" \"npm run testNode24Https\" \"npm run testNode24HttpsFips\" \"npm run testNode22Https\" \"npm run testNode22HttpsFips\" \"npm run testNode20Https\" \"npm run testNode20HttpsFips\" \"npm run testNode18Https\" && npm run testkills1116",
18
+ "test16Https11NoFips": "export CASS_LOOPBACK=https://cass-tests1116/api/|| set CASS_LOOPBACK=https://cass-tests1116/api/&& npm run testkills1116 && docker run -d --platform linux/amd64 --network cass-net --name cass-tests1116 -p451:443 -e CASS_LOOPBACK -e HTTPS=true -e HTTP2=false cassproject/cass:1.6 && wait-on https://localhost:451/api/ping && concurrently --kill-others-on-fail \"npm run testCypressHttps\" \"npm run testNode24Https\" \"npm run testNode22Https\" \"npm run testNode20Https\" \"npm run testNode18Https\" && npm run testkills1116",
19
+ "test16Fips": "export CASS_LOOPBACK=http://cass-testf16/api/|| set CASS_LOOPBACK=http://cass-testf16/api/&& npm run testkillf16 && docker run -d --platform linux/amd64 --network cass-net -e CASS_LOOPBACK --name cass-testf16 -p83:80 cassproject/cass:1.6 && wait-on http://localhost:83/api/ping && concurrently --kill-others-on-fail \"npm run testCypress\" \"npm run testNode24\" \"npm run testNode24Fips\" \"npm run testNode24ForceFips\" \"npm run testNode22\" \"npm run testNode22Fips\" \"npm run testNode22ForceFips\" \"npm run testNode20\" \"npm run testNode20Fips\" \"npm run testNode20ForceFips\" \"npm run testNode18\" && npm run testkillf16",
20
+ "test16": "export CASS_LOOPBACK=http://cass-test16/api/|| set CASS_LOOPBACK=http://cass-test16/api/&& npm run testkill16 && docker run -d --platform linux/amd64 --network cass-net -e CASS_LOOPBACK --name cass-test16 -p82:80 cassproject/cass:1.6 && wait-on http://localhost:82/api/ping && concurrently --kill-others-on-fail \"npm run testCypress\" \"npm run testNode24\" \"npm run testNode24Fips\" \"npm run testNode22\" \"npm run testNode22Fips\" \"npm run testNode20\" \"npm run testNode20Fips\" \"npm run testNode18\" && npm run testkill16",
21
+ "test16NoFips": "export CASS_LOOPBACK=http://cass-test16/api/|| set CASS_LOOPBACK=http://cass-test16/api/&& npm run testkill16 && docker run -d --platform linux/amd64 --network cass-net -e CASS_LOOPBACK --name cass-test16 -p82:80 cassproject/cass:1.6 && wait-on http://localhost:82/api/ping && concurrently --kill-others-on-fail \"npm run testCypress\" \"npm run testNode24\" \"npm run testNode22\" \"npm run testNode20\" \"npm run testNode18\" && npm run testkill16",
22
22
  "test15HttpsFips": "export TESTLEVEL=15|| set TESTLEVEL=15&& export CASS_LOOPBACK=https://cass-testsf15/api/|| set CASS_LOOPBACK=https://cass-testsf15/api/&& npm run testkillsf15 && docker run -d --platform linux/amd64 --network cass-net --name cass-testsf15 -p446:443 -e CASS_LOOPBACK -e HTTPS=true cassproject/cass:1.5.75 && wait-on https://localhost:446/api/ping && concurrently --kill-others-on-fail \"npm run testCypressHttps\" \"npm run testNode24Https\" \"npm run testNode24HttpsFips\" \"npm run testNode22Https\" \"npm run testNode22HttpsFips\" \"npm run testNode22HttpsForceFips\" \"npm run testNode20Https\" \"npm run testNode20HttpsFips\" \"npm run testNode20HttpsForceFips\" \"npm run testNode18Https\" && npm run testkillsf15",
23
23
  "test15Https11Fips": "export TESTLEVEL=15|| set TESTLEVEL=15&& export CASS_LOOPBACK=https://cass-testsf1115/api/|| set CASS_LOOPBACK=https://cass-testsf1115/api/&& npm run testkillsf1115 && docker run -d --platform linux/amd64 --network cass-net --name cass-testsf1115 -p447:443 -e CASS_LOOPBACK -e HTTPS=true -e HTTP2=false cassproject/cass:1.5.75 && wait-on https://localhost:447/api/ping && concurrently --kill-others-on-fail \"npm run testCypressHttps\" \"npm run testNode24Https\" \"npm run testNode24HttpsFips\" \"npm run testNode24HttpsForceFips\" \"npm run testNode22Https\" \"npm run testNode22HttpsFips\" \"npm run testNode22HttpsForceFips\" \"npm run testNode20Https\" \"npm run testNode20HttpsFips\" \"npm run testNode20HttpsForceFips\" \"npm run testNode18Https\" && npm run testkillsf1115",
24
24
  "test15Https": "export TESTLEVEL=15|| set TESTLEVEL=15&& export CASS_LOOPBACK=https://cass-tests15/api/|| set CASS_LOOPBACK=https://cass-tests15/api/&& npm run testkills15 && docker run -d --platform linux/amd64 --network cass-net --name cass-tests15 -p444:443 -e CASS_LOOPBACK -e HTTPS=true cassproject/cass:1.5.75 && wait-on https://localhost:444/api/ping && concurrently --kill-others-on-fail \"npm run testCypressHttps\" \"npm run testNode24Https\" \"npm run testNode24HttpsFips\" \"npm run testNode22Https\" \"npm run testNode22HttpsFips\" \"npm run testNode20Https\" \"npm run testNode20HttpsFips\" \"npm run testNode18Https\" && npm run testkills15",
@@ -141,13 +141,10 @@
141
141
  ],
142
142
  "dependencies": {
143
143
  "base64-arraybuffer": "^1.0.2",
144
- "forge": "^2.3.0",
145
144
  "jsonld": "^9.0.0",
146
145
  "node-forge": "^1.3.3",
147
146
  "papaparse": "^5.5.3",
148
- "pem-jwk": "^2.0.0",
149
147
  "promise-worker": "^2.0.1",
150
- "rdf-canonize": "^5.0.0",
151
148
  "web-worker": "1.3.0"
152
149
  },
153
150
  "files": [
@@ -172,25 +169,20 @@
172
169
  },
173
170
  "homepage": "https://github.com/cassproject/cass-npm#readme",
174
171
  "devDependencies": {
175
- "@cypress/browserify-preprocessor": "^3.0.2",
176
- "@cypress/vite-dev-server": "^7.2.0",
177
172
  "@cypress/webpack-preprocessor": "^7.0.2",
178
173
  "chai": "^4.5.0",
179
174
  "concurrently": "^9.2.1",
180
- "convert-hrtime": "^5.0.0",
181
175
  "cypress": "^15.10.0",
182
176
  "cypress-fail-fast": "^7.1.1",
183
- "eslint": "^9.39.2",
177
+ "eslint": "^10.0.1",
184
178
  "fake-indexeddb": "^6.2.5",
185
179
  "mocha": "^11.7.5",
186
180
  "node-polyfill-webpack-plugin": "^4.1.0",
187
- "nodemon": "^3.1.11",
181
+ "nodemon": "^3.1.14",
188
182
  "nyc": "^17.1.0",
189
183
  "sinon": "^21.0.1",
190
- "url-polyfill": "^1.1.14",
191
- "wait-on": "^9.0.3",
192
- "webpack": "^5.105.0",
193
- "webpack-cli": "^6.0.1"
184
+ "wait-on": "^9.0.4",
185
+ "webpack": "^5.105.2"
194
186
  },
195
187
  "packageManager": "npm@11.3.0+sha512.96eb611483f49c55f7fa74df61b588de9e213f80a256728e6798ddc67176c7b07e4a1cfc7de8922422cbce02543714367037536955221fa451b0c4fefaf20c66"
196
188
  }
@@ -1,4 +1,4 @@
1
- let pemJwk = require("pem-jwk");
1
+
2
2
  let forge = require("node-forge");
3
3
  /**
4
4
  * Helper classes for dealing with RSA Public Keys.
@@ -25,7 +25,7 @@ module.exports = class EcPk {
25
25
  */
26
26
  static fromPem(pem) {
27
27
  let pk = EcPk.cache[pem];
28
- if (pk != null)
28
+ if (pk != null)
29
29
  return pk;
30
30
  pk = new EcPk();
31
31
  try {
@@ -69,11 +69,11 @@ module.exports = class EcPk {
69
69
  * @return {string} PEM encoded public key without whitespace.
70
70
  * @method toPkcs1Pem
71
71
  */
72
- toPkcs1Pem = function() {
72
+ toPkcs1Pem = function () {
73
73
  return forge.pki
74
74
  .publicKeyToRSAPublicKeyPem(this.pk)
75
- .replace(/\r/g, "")
76
- .replace(/\n/g, "");
75
+ .replace(/\r/g, "")
76
+ .replace(/\n/g, "");
77
77
  };
78
78
  /**
79
79
  * Encodes the public key into a PEM encoded SubjectPublicKeyInfo (PKCS#8) formatted RSA Public Key.
@@ -82,16 +82,30 @@ module.exports = class EcPk {
82
82
  * @return {string} PEM encoded public key without whitespace.
83
83
  * @method toPkcs8Pem
84
84
  */
85
- toPkcs8Pem = function() {
85
+ toPkcs8Pem = function () {
86
86
  return forge.pki
87
87
  .publicKeyToPem(this.pk)
88
- .replace(/\r/g, "")
89
- .replace(/\n/g, "");
88
+ .replace(/\r/g, "")
89
+ .replace(/\n/g, "");
90
90
  };
91
91
 
92
92
  toJwk() {
93
- if (this.jwk == null)
94
- this.jwk = pemJwk.pem2jwk(forge.pki.publicKeyToPem(this.pk));
93
+ if (this.jwk == null) {
94
+ const bnToBase64Url = (bn) => {
95
+ let hex = bn.toString(16);
96
+ if (hex.length % 2 !== 0) {
97
+ hex = '0' + hex;
98
+ }
99
+ const bytes = forge.util.hexToBytes(hex);
100
+ const b64 = forge.util.encode64(bytes);
101
+ return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
102
+ };
103
+ this.jwk = {
104
+ kty: "RSA",
105
+ n: bnToBase64Url(this.pk.n),
106
+ e: bnToBase64Url(this.pk.e)
107
+ };
108
+ }
95
109
  return this.jwk;
96
110
  }
97
111
  /**
@@ -101,7 +115,7 @@ module.exports = class EcPk {
101
115
  * @method fingerprint
102
116
  */
103
117
  fingerprint() {
104
- return forge.ssh.getPublicKeyFingerprint(this.pk, {encoding:"hex"});
118
+ return forge.ssh.getPublicKeyFingerprint(this.pk, { encoding: "hex" });
105
119
  }
106
120
  verify(bytes, decode64) {
107
121
  return this.pk.verify(bytes, decode64);
@@ -1,4 +1,4 @@
1
- let pemJwk = require("pem-jwk");
1
+
2
2
  let forge = require("node-forge");
3
3
  let EcPk = require("./EcPk.js");
4
4
  require("../../../../org/cassproject/general/AuditLogger.js");
@@ -48,8 +48,8 @@ module.exports = class EcPpk {
48
48
  * @static
49
49
  */
50
50
  static generateKeyAsync(callback) {
51
- return new Promise((resolve,reject)=>{
52
- forge.pki.rsa.generateKeyPair({workers:-1}, function(err, keypair) {
51
+ return new Promise((resolve, reject) => {
52
+ forge.pki.rsa.generateKeyPair({ workers: -1 }, function (err, keypair) {
53
53
  let ppk = new EcPpk();
54
54
  ppk.ppk = keypair.privateKey;
55
55
  if (callback != null)
@@ -66,7 +66,7 @@ module.exports = class EcPpk {
66
66
  * @static
67
67
  */
68
68
  static generateKey() {
69
- let keypair = forge.pki.rsa.generateKeyPair({workers:-1}, null);
69
+ let keypair = forge.pki.rsa.generateKeyPair({ workers: -1 }, null);
70
70
  let ppk = new EcPpk();
71
71
  ppk.ppk = keypair.privateKey;
72
72
  return ppk;
@@ -108,11 +108,11 @@ module.exports = class EcPpk {
108
108
  * @return {string} PEM encoded public key without whitespace.
109
109
  * @method toPkcs1Pem
110
110
  */
111
- toPkcs1Pem = function() {
111
+ toPkcs1Pem = function () {
112
112
  return forge.pki
113
113
  .privateKeyToPem(this.ppk)
114
- .replace(/\r/g, "")
115
- .replace(/\n/g, "");
114
+ .replace(/\r/g, "")
115
+ .replace(/\n/g, "");
116
116
  };
117
117
  /**
118
118
  * Encodes the private key into a PEM encoded PrivateKeyInfo (PKCS#8) formatted RSA Public Key.
@@ -121,22 +121,42 @@ module.exports = class EcPpk {
121
121
  * @return {string} PEM encoded public key without whitespace.
122
122
  * @method toPkcs8Pem
123
123
  */
124
- toPkcs8Pem = function() {
124
+ toPkcs8Pem = function () {
125
125
  return forge.pki
126
126
  .privateKeyInfoToPem(
127
127
  forge.pki.wrapRsaPrivateKey(
128
128
  forge.pki.privateKeyToAsn1(this.ppk)
129
129
  )
130
130
  )
131
- .replace(/\r/g, "")
132
- .replace(/\n/g, "");
131
+ .replace(/\r/g, "")
132
+ .replace(/\n/g, "");
133
133
  };
134
134
  toJwk() {
135
- if (this.jwk == null)
136
- this.jwk = pemJwk.pem2jwk(forge.pki.privateKeyToPem(this.ppk));
135
+ if (this.jwk == null) {
136
+ const bnToBase64Url = (bn) => {
137
+ let hex = bn.toString(16);
138
+ if (hex.length % 2 !== 0) {
139
+ hex = '0' + hex;
140
+ }
141
+ const bytes = forge.util.hexToBytes(hex);
142
+ const b64 = forge.util.encode64(bytes);
143
+ return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
144
+ };
145
+ this.jwk = {
146
+ kty: "RSA",
147
+ n: bnToBase64Url(this.ppk.n),
148
+ e: bnToBase64Url(this.ppk.e),
149
+ d: bnToBase64Url(this.ppk.d),
150
+ p: bnToBase64Url(this.ppk.p),
151
+ q: bnToBase64Url(this.ppk.q),
152
+ dp: bnToBase64Url(this.ppk.dP),
153
+ dq: bnToBase64Url(this.ppk.dQ),
154
+ qi: bnToBase64Url(this.ppk.qInv)
155
+ };
156
+ }
137
157
  return this.jwk;
138
158
  }
139
- toPkcs8 = function() {
159
+ toPkcs8 = function () {
140
160
  return forge.pki.wrapRsaPrivateKey(
141
161
  forge.pki.privateKeyToAsn1(this.ppk)
142
162
  );
@@ -162,8 +182,7 @@ module.exports = class EcPpk {
162
182
  */
163
183
  inArray(ppks) {
164
184
  for (let ppk of ppks) {
165
- if (ppk.equals(this))
166
- {
185
+ if (ppk.equals(this)) {
167
186
  return true;
168
187
  }
169
188
  }
@@ -110,7 +110,8 @@ module.exports = class CTDLASNCSVConceptImport {
110
110
  endpoint,
111
111
  eim,
112
112
  progressionsFlag,
113
- validationRules
113
+ validationRules,
114
+ duplicateChecks
114
115
  ) {
115
116
  if (eim === undefined || eim == null)
116
117
  eim = EcIdentityManager.default;
@@ -155,6 +156,7 @@ module.exports = class CTDLASNCSVConceptImport {
155
156
  const terms = JSON.parse(JSON.stringify((await EcRemote.getExpectingObject("https://schema.cassproject.org/0.4/jsonld1.1/ceasn2cassConceptsTerms"))));
156
157
  let schemeArray = [];
157
158
  let concepts = [];
159
+ let conceptCtids = {};
158
160
  let rowsWithErrors = new Set(); // Track rows that have errors
159
161
  for (let each = 0; each < tabularData.length; each++) {
160
162
  let pretranslatedE = tabularData[each];
@@ -461,6 +463,7 @@ module.exports = class CTDLASNCSVConceptImport {
461
463
  "schema:dateModified"
462
464
  ] = new Date().toISOString();
463
465
  concepts.push(f);
466
+ conceptCtids[f.shortId()] = pretranslatedE["ceterms:ctid"] || pretranslatedE["@id"];
464
467
  } else if (
465
468
  pretranslatedE["@type"] == null ||
466
469
  pretranslatedE["@type"] == ""
@@ -476,6 +479,13 @@ module.exports = class CTDLASNCSVConceptImport {
476
479
  continue;
477
480
  }
478
481
  }
482
+ // Run duplicate CTID checks if any were provided by the caller
483
+ const checks = duplicateChecks || [];
484
+ const conceptIds = concepts.map(c => c.shortId());
485
+ for (const check of checks) {
486
+ const checkErrors = await check(repo, conceptIds, schemeArray, conceptCtids);
487
+ errors.push(...checkErrors);
488
+ }
479
489
  // If there are any errors, report them all at once
480
490
  if (errors.length > 0) {
481
491
  failure(errors);
@@ -494,7 +504,8 @@ module.exports = class CTDLASNCSVConceptImport {
494
504
  ceo,
495
505
  endpoint,
496
506
  eim,
497
- validationRules
507
+ validationRules,
508
+ duplicateChecks
498
509
  ) {
499
510
  Papa.parse(file, {
500
511
  header: true,
@@ -525,6 +536,7 @@ module.exports = class CTDLASNCSVConceptImport {
525
536
  const terms = JSON.parse(JSON.stringify((await EcRemote.getExpectingObject("https://schema.cassproject.org/0.4/jsonld1.1/ceasn2cassConceptsTerms"))));
526
537
  let schemeArray = [];
527
538
  let concepts = [];
539
+ let conceptCtids = {};
528
540
  let rowsWithErrors = new Set(); // Track rows that have errors
529
541
  for (let each = 0; each < tabularData.length; each++) {
530
542
  let pretranslatedE = tabularData[each];
@@ -788,6 +800,7 @@ module.exports = class CTDLASNCSVConceptImport {
788
800
  ] = new Date().toISOString();
789
801
  f.subType = "Progression";
790
802
  concepts.push(f);
803
+ conceptCtids[f.shortId()] = pretranslatedE["ceterms:ctid"] || pretranslatedE["@id"];
791
804
  } else if (
792
805
  pretranslatedE["@type"] == null ||
793
806
  pretranslatedE["@type"] == ""
@@ -803,6 +816,13 @@ module.exports = class CTDLASNCSVConceptImport {
803
816
  continue;
804
817
  }
805
818
  }
819
+ // Run duplicate CTID checks if any were provided by the caller
820
+ const checks = duplicateChecks || [];
821
+ const conceptIds = concepts.map(c => c.shortId());
822
+ for (const check of checks) {
823
+ const checkErrors = await check(repo, conceptIds, schemeArray, conceptCtids);
824
+ errors.push(...checkErrors);
825
+ }
806
826
  // If there are any errors, report them all at once
807
827
  if (errors.length > 0) {
808
828
  failure(errors);
@@ -134,7 +134,8 @@ module.exports = class CTDLASNCSVImport {
134
134
  eim,
135
135
  collectionsFlag,
136
136
  skip,
137
- validationRules
137
+ validationRules,
138
+ duplicateChecks
138
139
  ) {
139
140
  if (eim === undefined || eim == null)
140
141
  eim = EcIdentityManager.default;
@@ -185,6 +186,7 @@ module.exports = class CTDLASNCSVImport {
185
186
  let competencyRows = {};
186
187
  let relations = [];
187
188
  let relationById = {};
189
+ let competencyCtids = {};
188
190
  let rowsWithErrors = new Set(); // Track rows that have errors to avoid duplicate processing
189
191
  for (let i = 0; i < tabularData.length; i++) {
190
192
  if (!tabularData[i]) {
@@ -701,6 +703,7 @@ module.exports = class CTDLASNCSVImport {
701
703
  f["ceasn:hasChild"] = null;
702
704
  setVersionIdentifier(f);
703
705
  competencies.push(f);
706
+ competencyCtids[f.shortId()] = pretranslatedE["ceterms:ctid"] || pretranslatedE["@id"];
704
707
  competencyRows[f.shortId()] = e;
705
708
  } else if (
706
709
  pretranslatedE["@type"] == null ||
@@ -717,6 +720,13 @@ module.exports = class CTDLASNCSVImport {
717
720
  continue;
718
721
  }
719
722
  }
723
+ // Run duplicate CTID checks if any were provided by the caller
724
+ const checks = duplicateChecks || [];
725
+ const competencyIds = competencies.map(comp => comp.shortId());
726
+ for (const check of checks) {
727
+ const checkErrors = await check(repo, competencyIds, frameworkArray, competencyCtids);
728
+ errors.push(...checkErrors);
729
+ }
720
730
  // If there are any errors, report them all at once
721
731
  if (errors.length > 0) {
722
732
  failure(errors);
@@ -736,7 +746,8 @@ module.exports = class CTDLASNCSVImport {
736
746
  endpoint,
737
747
  eim,
738
748
  skip,
739
- validationRules
749
+ validationRules,
750
+ duplicateChecks
740
751
  ) {
741
752
  Papa.parse(file, {
742
753
  header: true,
@@ -773,6 +784,7 @@ module.exports = class CTDLASNCSVImport {
773
784
  let competencyRows = {};
774
785
  let relations = [];
775
786
  let relationById = {};
787
+ let competencyCtids = {};
776
788
  let rowsWithErrors = new Set(); // Track rows that have errors
777
789
  for (let i = 0; i < tabularData.length; i++) {
778
790
  let pretranslatedE = tabularData[i];
@@ -1140,6 +1152,7 @@ module.exports = class CTDLASNCSVImport {
1140
1152
  f["ceterms:isMemberOf"] = null;
1141
1153
  setVersionIdentifier(f);
1142
1154
  competencies.push(f);
1155
+ competencyCtids[f.shortId()] = pretranslatedE["ceterms:ctid"] || pretranslatedE["@id"];
1143
1156
  competencyRows[f.shortId()] = e;
1144
1157
  } else if (
1145
1158
  pretranslatedE["@type"] == null ||
@@ -1156,6 +1169,13 @@ module.exports = class CTDLASNCSVImport {
1156
1169
  continue;
1157
1170
  }
1158
1171
  }
1172
+ // Run duplicate CTID checks if any were provided by the caller
1173
+ const checks = duplicateChecks || [];
1174
+ const competencyIds = competencies.map(comp => comp.shortId());
1175
+ for (const check of checks) {
1176
+ const checkErrors = await check(repo, competencyIds, frameworkArray, competencyCtids);
1177
+ errors.push(...checkErrors);
1178
+ }
1159
1179
  // If there are any errors, report them all at once
1160
1180
  if (errors.length > 0) {
1161
1181
  failure(errors);
@@ -0,0 +1,63 @@
1
+ let EcPpk = require("../com/eduworks/ec/crypto/EcPpk.js");
2
+ let EcRsaOaepAsync = require("../com/eduworks/ec/crypto/EcRsaOaepAsync.js");
3
+ let EcRsaOaep = require("../com/eduworks/ec/crypto/EcRsaOaep.js");
4
+ let EcAes = require("../com/eduworks/ec/crypto/EcAes.js");
5
+ let chai = require("chai");
6
+
7
+ let assert = chai.assert;
8
+
9
+ describe("EcCrypto JWK", () => {
10
+ let ppk = null;
11
+ let pk = null;
12
+
13
+ before(async function () {
14
+ this.timeout(10000);
15
+ ppk = await EcPpk.generateKeyAsync();
16
+ pk = ppk.toPk();
17
+ });
18
+
19
+ it('EcPpk.toJwk is valid', () => {
20
+ let jwk = ppk.toJwk();
21
+ assert.isNotNull(jwk);
22
+ assert.isObject(jwk);
23
+ assert.equal(jwk.kty, "RSA");
24
+ assert.isString(jwk.n);
25
+ assert.isString(jwk.e);
26
+ assert.isString(jwk.d);
27
+ assert.isString(jwk.p);
28
+ assert.isString(jwk.q);
29
+ });
30
+
31
+ it('EcPk.toJwk is valid', () => {
32
+ let jwk = pk.toJwk();
33
+ assert.isNotNull(jwk);
34
+ assert.isObject(jwk);
35
+ assert.equal(jwk.kty, "RSA");
36
+ assert.isString(jwk.n);
37
+ assert.isString(jwk.e);
38
+ assert.isUndefined(jwk.d);
39
+ });
40
+
41
+ it('EcRsaOaepAsync.encrypt/decrypt works with generated JWK', async () => {
42
+ let randomString = EcAes.newIv(256).substring(0, 190);
43
+
44
+ // The verify functions in EcRsaOaepAsync automatically use toJwk()
45
+ // behind the scenes when ppk.key is null, so by passing our newly
46
+ // generated keys, we are implicitly testing that the JWK format generated
47
+ // by `.toJwk()` is perfectly valid for use with crypto.subtle.
48
+
49
+ let encrypted = await EcRsaOaepAsync.encrypt(pk, randomString);
50
+ let decrypted = await EcRsaOaep.decrypt(ppk, encrypted);
51
+
52
+ assert.isTrue(randomString === decrypted, "Decrypted string should match original");
53
+ });
54
+
55
+ it('EcRsaOaepAsync.signSha256/verifySha256 works with generated JWK', async () => {
56
+ let randomString = EcAes.newIv(256 * 4);
57
+
58
+ let signature = await EcRsaOaepAsync.signSha256(ppk, randomString);
59
+ let verified = await EcRsaOaep.verifySha256(pk, randomString, signature);
60
+
61
+ assert.isTrue(verified, "Signature should be successfully verified");
62
+ });
63
+ });