openvibers/openvibe.contracts Commits
v0.5.1: every running service's manifest accepts >=0.2.0 <1.0.0; openvibe-contracts-check also reads { capability: 'x' } route options
Changes
@@@ -7,7 +7,8 @@** Fails when* - the installed openvibe-contracts version is outside the service manifest's contractRanges-- * - the code guards a capability (requireCapability('x') / guard('x') / capabilities.check(.., 'x'))++ * - the code guards a capability (requireCapability('x') / guard('x') / capabilities.check(.., 'x') /++ * { capability: 'x' } route options)* that the contracts don't define, that is retired, or that another service owns* - the code validates against a contract id (validate('x') / assertValid('x')) that doesn't exist* Prints what it checked so a green run says something.@@@ -56,14 +57,14 @@ for (const d of srcDirs) (function walk(dir) {}})(d);--const CAP_RE = /\b(?:requireCapability|guard)\(\s*['"]([a-z][a-z0-9_.]+)['"]|capabilities\.check\([^,]+,\s*['"]([a-z][a-z0-9_.]+)['"]/g;++const CAP_RE = /\b(?:requireCapability|guard)\(\s*['"]([a-z][a-z0-9_.]+)['"]|capabilities\.check\([^,]+,\s*['"]([a-z][a-z0-9_.]+)['"]|\bcapability:\s*['"]([a-z][a-z0-9_.]+)['"]/g;const CONTRACT_RE = /\b(?:validate|assertValid)\(\s*['"]([a-z][a-z0-9-]*\.[a-z0-9-]+(?:@\d+)?)['"]/g;const used = new Map();const refs = new Map();for (const f of files) {const text = fs.readFileSync(f, 'utf8');let m;-- while ((m = CAP_RE.exec(text))) { const id = m[1] || m[2]; (used.get(id) || used.set(id, []).get(id)).push(path.relative(process.cwd(), f)); }++ while ((m = CAP_RE.exec(text))) { const id = m[1] || m[2] || m[3]; (used.get(id) || used.set(id, []).get(id)).push(path.relative(process.cwd(), f)); }while ((m = CONTRACT_RE.exec(text))) { (refs.get(m[1]) || refs.set(m[1], []).get(m[1])).push(path.relative(process.cwd(), f)); }}for (const [id, where] of used) {@@@ -1,7 +1,7 @@{"version": "0.1.0","contractRanges": {-- "openvibe-contracts": "^0.1.0"++ "openvibe-contracts": ">=0.2.0 <1.0.0"},"id": "contracts","name": "OpenVibe.Contracts",@@@ -1,7 +1,7 @@{"version": "0.1.0","contractRanges": {-- "openvibe-contracts": "^0.1.0"++ "openvibe-contracts": ">=0.2.0 <1.0.0"},"id": "games","name": "OpenVibe.Games",@@@ -1,7 +1,7 @@{"version": "0.1.0","contractRanges": {-- "openvibe-contracts": "^0.1.0"++ "openvibe-contracts": ">=0.2.0 <1.0.0"},"id": "media","name": "OpenVibe.Media",@@@ -1,7 +1,7 @@{"version": "0.1.0","contractRanges": {-- "openvibe-contracts": "^0.1.0"++ "openvibe-contracts": ">=0.2.0 <1.0.0"},"id": "sites","name": "OpenVibe.Sites",@@@ -1,7 +1,7 @@{"version": "0.1.0","contractRanges": {-- "openvibe-contracts": "^0.1.0"++ "openvibe-contracts": ">=0.2.0 <1.0.0"},"id": "tools","name": "OpenVibe.Tools",@@@ -1,6 +1,6 @@{"name": "openvibe-contracts",-- "version": "0.5.0",++ "version": "0.5.1","description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.","main": "index.js","types": "generated/typescript/index.d.ts",@@@ -190,6 +190,8 @@ await assert.rejects(failing.getToken(), /401: invalid_client/);};let c = check({ 'a.js': "router.post('/x', guard('network.coins.credit'), h); validate('identity.subject-ref@1', v);" });ok(c.code === 0 && /1 capabilities enforced \[network.coins.credit\]/.test(c.out), 'owned capability passes: ' + c.out);++ c = check({ 'a.js': "router.post('/', tenantAuth({ capability: 'media.object.upload' }), h)" }, 'media');++ ok(c.code === 0 && /\[media.object.upload\]/.test(c.out), 'route-option capabilities are checked too');c = check({ 'a.js': "requireCapability('network.coins.mint')" });ok(c.code === 1 && /not defined/.test(c.out), 'unknown capability fails');c = check({ 'a.js': "requireCapability('media.object.upload')" });
@@ -7,7 +7,8 @@
@@ -7,7 +7,8 @@
*
*
* Fails when
* Fails when
* - the installed openvibe-contracts version is outside the service manifest's contractRanges
* - the installed openvibe-contracts version is outside the service manifest's contractRanges
- * - the code guards a capability (requireCapability('x') / guard('x') / capabilities.check(.., 'x'))
+ * - the code guards a capability (requireCapability('x') / guard('x') / capabilities.check(.., 'x') /
+ * { capability: 'x' } route options)
* that the contracts don't define, that is retired, or that another service owns
* that the contracts don't define, that is retired, or that another service owns
* - the code validates against a contract id (validate('x') / assertValid('x')) that doesn't exist
* - the code validates against a contract id (validate('x') / assertValid('x')) that doesn't exist
* Prints what it checked so a green run says something.
* Prints what it checked so a green run says something.
@@ -56,14 +57,14 @@ for (const d of srcDirs) (function walk(dir) {
@@ -56,14 +57,14 @@ for (const d of srcDirs) (function walk(dir) {
}
}
})(d);
})(d);
-const CAP_RE = /\b(?:requireCapability|guard)\(\s*['"]([a-z][a-z0-9_.]+)['"]|capabilities\.check\([^,]+,\s*['"]([a-z][a-z0-9_.]+)['"]/g;
+const CAP_RE = /\b(?:requireCapability|guard)\(\s*['"]([a-z][a-z0-9_.]+)['"]|capabilities\.check\([^,]+,\s*['"]([a-z][a-z0-9_.]+)['"]|\bcapability:\s*['"]([a-z][a-z0-9_.]+)['"]/g;
const CONTRACT_RE = /\b(?:validate|assertValid)\(\s*['"]([a-z][a-z0-9-]*\.[a-z0-9-]+(?:@\d+)?)['"]/g;
const CONTRACT_RE = /\b(?:validate|assertValid)\(\s*['"]([a-z][a-z0-9-]*\.[a-z0-9-]+(?:@\d+)?)['"]/g;
const used = new Map();
const used = new Map();
const refs = new Map();
const refs = new Map();
for (const f of files) {
for (const f of files) {
const text = fs.readFileSync(f, 'utf8');
const text = fs.readFileSync(f, 'utf8');
let m;
let m;
- while ((m = CAP_RE.exec(text))) { const id = m[1] || m[2]; (used.get(id) || used.set(id, []).get(id)).push(path.relative(process.cwd(), f)); }
+ while ((m = CAP_RE.exec(text))) { const id = m[1] || m[2] || m[3]; (used.get(id) || used.set(id, []).get(id)).push(path.relative(process.cwd(), f)); }
while ((m = CONTRACT_RE.exec(text))) { (refs.get(m[1]) || refs.set(m[1], []).get(m[1])).push(path.relative(process.cwd(), f)); }
while ((m = CONTRACT_RE.exec(text))) { (refs.get(m[1]) || refs.set(m[1], []).get(m[1])).push(path.relative(process.cwd(), f)); }
}
}
for (const [id, where] of used) {
for (const [id, where] of used) {
@@ -1,7 +1,7 @@
@@ -1,7 +1,7 @@
{
{
"version": "0.1.0",
"version": "0.1.0",
"contractRanges": {
"contractRanges": {
- "openvibe-contracts": "^0.1.0"
+ "openvibe-contracts": ">=0.2.0 <1.0.0"
},
},
"id": "contracts",
"id": "contracts",
"name": "OpenVibe.Contracts",
"name": "OpenVibe.Contracts",
@@ -1,7 +1,7 @@
@@ -1,7 +1,7 @@
{
{
"version": "0.1.0",
"version": "0.1.0",
"contractRanges": {
"contractRanges": {
- "openvibe-contracts": "^0.1.0"
+ "openvibe-contracts": ">=0.2.0 <1.0.0"
},
},
"id": "games",
"id": "games",
"name": "OpenVibe.Games",
"name": "OpenVibe.Games",
@@ -1,7 +1,7 @@
@@ -1,7 +1,7 @@
{
{
"version": "0.1.0",
"version": "0.1.0",
"contractRanges": {
"contractRanges": {
- "openvibe-contracts": "^0.1.0"
+ "openvibe-contracts": ">=0.2.0 <1.0.0"
},
},
"id": "media",
"id": "media",
"name": "OpenVibe.Media",
"name": "OpenVibe.Media",
@@ -1,7 +1,7 @@
@@ -1,7 +1,7 @@
{
{
"version": "0.1.0",
"version": "0.1.0",
"contractRanges": {
"contractRanges": {
- "openvibe-contracts": "^0.1.0"
+ "openvibe-contracts": ">=0.2.0 <1.0.0"
},
},
"id": "sites",
"id": "sites",
"name": "OpenVibe.Sites",
"name": "OpenVibe.Sites",
@@ -1,7 +1,7 @@
@@ -1,7 +1,7 @@
{
{
"version": "0.1.0",
"version": "0.1.0",
"contractRanges": {
"contractRanges": {
- "openvibe-contracts": "^0.1.0"
+ "openvibe-contracts": ">=0.2.0 <1.0.0"
},
},
"id": "tools",
"id": "tools",
"name": "OpenVibe.Tools",
"name": "OpenVibe.Tools",
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.5.0",
+ "version": "0.5.1",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"main": "index.js",
"main": "index.js",
"types": "generated/typescript/index.d.ts",
"types": "generated/typescript/index.d.ts",
@@ -190,6 +190,8 @@ await assert.rejects(failing.getToken(), /401: invalid_client/);
@@ -190,6 +190,8 @@ await assert.rejects(failing.getToken(), /401: invalid_client/);
};
};
let c = check({ 'a.js': "router.post('/x', guard('network.coins.credit'), h); validate('identity.subject-ref@1', v);" });
let c = check({ 'a.js': "router.post('/x', guard('network.coins.credit'), h); validate('identity.subject-ref@1', v);" });
ok(c.code === 0 && /1 capabilities enforced \[network.coins.credit\]/.test(c.out), 'owned capability passes: ' + c.out);
ok(c.code === 0 && /1 capabilities enforced \[network.coins.credit\]/.test(c.out), 'owned capability passes: ' + c.out);
+ c = check({ 'a.js': "router.post('/', tenantAuth({ capability: 'media.object.upload' }), h)" }, 'media');
+ ok(c.code === 0 && /\[media.object.upload\]/.test(c.out), 'route-option capabilities are checked too');
c = check({ 'a.js': "requireCapability('network.coins.mint')" });
c = check({ 'a.js': "requireCapability('network.coins.mint')" });
ok(c.code === 1 && /not defined/.test(c.out), 'unknown capability fails');
ok(c.code === 1 && /not defined/.test(c.out), 'unknown capability fails');
c = check({ 'a.js': "requireCapability('media.object.upload')" });
c = check({ 'a.js': "requireCapability('media.object.upload')" });