openvibers/openvibe.network Commits
Principals: openre, media and community hold live.lineage.resolve for audience openvibe.live (roadmap §10.5/§15.10, D20-R1), so OpenRe, Media and Community's Pulse can ask Live's canonical channel/owner resolver (GET|POST /internal/lineage/resolve, token only) instead of keeping their own channel mappings; production had no grant for it. Seeded at boot like every default grant. test/principals.test.js: each of the three gets a token for openvibe.live carrying it, and nobody else holds it
Changes
@@@ -130,6 +130,10 @@ const DEFAULT_GRANTS = [['live', 'community.paste.create', 'openvibe.community', []],['live', 'community.paste.write', 'openvibe.community', []],['live', 'community.paste.moderate', 'openvibe.community', []],++ // One canonical channel/owner resolver (roadmap §10.5/§15.10, D20-R1): OpenRe, Media and Community (Pulse)++ // resolve channels, streams, VODs and clips through Live's /internal/lineage/resolve instead of their own++ // channel mappings.++ ...['openre', 'media', 'community'].map(c => [c, 'live.lineage.resolve', 'openvibe.live', []]),// Wave 5 remainder: Live comments on its own entities, publishes stream/VOD items to Pulse, and// hides a thread when it takes the entity down.['live', 'community.comment.write', 'openvibe.community', []],@@@ -67,6 +67,19 @@ const server = http.createServer(app);db.prepare("UPDATE oauth_clients SET client_secret = 'openre-secret' WHERE client_id = 'openre'").run();t = await token({ client_id: 'openre', client_secret: 'openre-secret', audience: 'openvibe.network' });assert.strictEqual(t.status, 400); assert.strictEqual(t.body.error, 'invalid_scope', 'a client with no grants gets no token');++ // The canonical channel/owner resolver on Live (D20-R1): OpenRe, Media and Community hold it, nobody else.++ db.prepare("UPDATE oauth_clients SET client_secret = 'community-secret' WHERE client_id = 'community'").run();++ for (const [client, secret] of [['openre', 'openre-secret'], ['media', 'media-secret'], ['community', 'community-secret']]) {++ t = await token({ client_id: client, client_secret: secret, audience: 'openvibe.live' });++ assert.strictEqual(t.status, 200, `${client}: ${JSON.stringify(t.body)}`);++ assert.ok(t.body.scope.split(' ').includes('live.lineage.resolve'), `${client} may resolve lineage on Live`);++ const c = serviceAuth.verifyServiceToken(t.body.access_token, { publicKey: keys.publicKey, issuer: ISSUER, audience: 'openvibe.live' });++ assert.ok(c.ok && c.claims.sub === `svc:${client}` && c.claims.aud.includes('openvibe.live'));++ }++ t = await token({ client_id: 'live', client_secret: 'live-secret', audience: 'openvibe.live', scope: 'live.lineage.resolve' });++ assert.strictEqual(t.status, 400, 'Live does not grant itself its own resolver');++ const holders = db.prepare("SELECT client_id FROM principal_grants WHERE capability = 'live.lineage.resolve' AND audience = 'openvibe.live' AND revoked_at IS NULL ORDER BY client_id").all().map(x => x.client_id);++ assert.deepStrictEqual(holders, ['community', 'media', 'openre']);t = await token({ client_id: 'media', client_secret: 'media-secret', audience: 'openvibe.network' });assert.strictEqual(t.status, 200, JSON.stringify(t.body));assert.strictEqual(t.body.scope, 'identity.subject.resolve', 'Media resolves object owners to subjects, and nothing else here');
@@ -130,6 +130,10 @@ const DEFAULT_GRANTS = [
@@ -130,6 +130,10 @@ const DEFAULT_GRANTS = [
['live', 'community.paste.create', 'openvibe.community', []],
['live', 'community.paste.create', 'openvibe.community', []],
['live', 'community.paste.write', 'openvibe.community', []],
['live', 'community.paste.write', 'openvibe.community', []],
['live', 'community.paste.moderate', 'openvibe.community', []],
['live', 'community.paste.moderate', 'openvibe.community', []],
+ // One canonical channel/owner resolver (roadmap §10.5/§15.10, D20-R1): OpenRe, Media and Community (Pulse)
+ // resolve channels, streams, VODs and clips through Live's /internal/lineage/resolve instead of their own
+ // channel mappings.
+ ...['openre', 'media', 'community'].map(c => [c, 'live.lineage.resolve', 'openvibe.live', []]),
// Wave 5 remainder: Live comments on its own entities, publishes stream/VOD items to Pulse, and
// Wave 5 remainder: Live comments on its own entities, publishes stream/VOD items to Pulse, and
// hides a thread when it takes the entity down.
// hides a thread when it takes the entity down.
['live', 'community.comment.write', 'openvibe.community', []],
['live', 'community.comment.write', 'openvibe.community', []],
@@ -67,6 +67,19 @@ const server = http.createServer(app);
@@ -67,6 +67,19 @@ const server = http.createServer(app);
db.prepare("UPDATE oauth_clients SET client_secret = 'openre-secret' WHERE client_id = 'openre'").run();
db.prepare("UPDATE oauth_clients SET client_secret = 'openre-secret' WHERE client_id = 'openre'").run();
t = await token({ client_id: 'openre', client_secret: 'openre-secret', audience: 'openvibe.network' });
t = await token({ client_id: 'openre', client_secret: 'openre-secret', audience: 'openvibe.network' });
assert.strictEqual(t.status, 400); assert.strictEqual(t.body.error, 'invalid_scope', 'a client with no grants gets no token');
assert.strictEqual(t.status, 400); assert.strictEqual(t.body.error, 'invalid_scope', 'a client with no grants gets no token');
+ // The canonical channel/owner resolver on Live (D20-R1): OpenRe, Media and Community hold it, nobody else.
+ db.prepare("UPDATE oauth_clients SET client_secret = 'community-secret' WHERE client_id = 'community'").run();
+ for (const [client, secret] of [['openre', 'openre-secret'], ['media', 'media-secret'], ['community', 'community-secret']]) {
+ t = await token({ client_id: client, client_secret: secret, audience: 'openvibe.live' });
+ assert.strictEqual(t.status, 200, `${client}: ${JSON.stringify(t.body)}`);
+ assert.ok(t.body.scope.split(' ').includes('live.lineage.resolve'), `${client} may resolve lineage on Live`);
+ const c = serviceAuth.verifyServiceToken(t.body.access_token, { publicKey: keys.publicKey, issuer: ISSUER, audience: 'openvibe.live' });
+ assert.ok(c.ok && c.claims.sub === `svc:${client}` && c.claims.aud.includes('openvibe.live'));
+ }
+ t = await token({ client_id: 'live', client_secret: 'live-secret', audience: 'openvibe.live', scope: 'live.lineage.resolve' });
+ assert.strictEqual(t.status, 400, 'Live does not grant itself its own resolver');
+ const holders = db.prepare("SELECT client_id FROM principal_grants WHERE capability = 'live.lineage.resolve' AND audience = 'openvibe.live' AND revoked_at IS NULL ORDER BY client_id").all().map(x => x.client_id);
+ assert.deepStrictEqual(holders, ['community', 'media', 'openre']);
t = await token({ client_id: 'media', client_secret: 'media-secret', audience: 'openvibe.network' });
t = await token({ client_id: 'media', client_secret: 'media-secret', audience: 'openvibe.network' });
assert.strictEqual(t.status, 200, JSON.stringify(t.body));
assert.strictEqual(t.status, 200, JSON.stringify(t.body));
assert.strictEqual(t.body.scope, 'identity.subject.resolve', 'Media resolves object owners to subjects, and nothing else here');
assert.strictEqual(t.body.scope, 'identity.subject.resolve', 'Media resolves object owners to subjects, and nothing else here');