Javascript implementation of Union of Blessings
MultiPart: 1/3
Change-Id: Ifd68cadf17ac4a9d7e34076d503b1e48721acf8e
diff --git a/src/gen-vdl/v.io/x/ref/services/wspr/internal/app/index.js b/src/gen-vdl/v.io/x/ref/services/wspr/internal/app/index.js
index 072765b..0d99bb0 100644
--- a/src/gen-vdl/v.io/x/ref/services/wspr/internal/app/index.js
+++ b/src/gen-vdl/v.io/x/ref/services/wspr/internal/app/index.js
@@ -22,7 +22,6 @@
// Types:
var _type1 = new vdl.Type();
-var _type10 = new vdl.Type();
var _type2 = new vdl.Type();
var _type3 = new vdl.Type();
var _type4 = new vdl.Type();
@@ -41,9 +40,6 @@
_type1.kind = vdl.kind.LIST;
_type1.name = "";
_type1.elem = _typeRpcCallOption;
-_type10.kind = vdl.kind.LIST;
-_type10.name = "";
-_type10.elem = new security.WireBlessings()._type;
_type2.kind = vdl.kind.LIST;
_type2.name = "";
_type2.elem = new security.BlessingPattern()._type;
@@ -90,7 +86,6 @@
_typeRpcServerOption.name = "v.io/x/ref/services/wspr/internal/app.RpcServerOption";
_typeRpcServerOption.fields = [{name: "IsLeaf", type: vdl.types.BOOL}, {name: "ServesMountTable", type: vdl.types.BOOL}];
_type1.freeze();
-_type10.freeze();
_type2.freeze();
_type3.freeze();
_type4.freeze();
@@ -214,11 +209,6 @@
Controller.prototype.signature = function(ctx, serverCall, name) {
throw new Error('Method Signature not implemented');
};
-
-
-Controller.prototype.unionOfBlessings = function(ctx, serverCall, toJoin) {
- throw new Error('Method UnionOfBlessings not implemented');
-};
@@ -564,27 +554,6 @@
outStream: null,
tags: []
},
-
-
- {
- name: 'UnionOfBlessings',
- doc: "// UnionOfBlessings returns a Blessings object that carries the union of the provided blessings.",
- inArgs: [{
- name: 'toJoin',
- doc: "",
- type: _type10
- },
- ],
- outArgs: [{
- name: '',
- doc: "",
- type: new principal.BlessingsId()._type
- },
- ],
- inStream: null,
- outStream: null,
- tags: []
- },
]
};
diff --git a/src/security/blessings-util.js b/src/security/blessings-util.js
index 9f6ce4d..d876ac1 100644
--- a/src/security/blessings-util.js
+++ b/src/security/blessings-util.js
@@ -2,7 +2,16 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-var runtimeFromContext = require('../../src/runtime/runtime-from-context');
+var Blessings = require('../../src/security/blessings');
+var makeError = require('../../src/verror/make-errors');
+var actions = require('../../src/verror/actions');
+
+var InvalidUnionError = makeError('v.io/v23/security.errInvalidUnion',
+ actions.NO_RETRY, {
+ 'en':
+ '{1:}{2:} cannot create union of blessings bound to different public keys',
+ }, []);
+
/**
* @fileoverview Blessings related utilities that don't belong on the
@@ -27,21 +36,58 @@
* provided blessings.
* @param {module:vanadium.context.Context} ctx The context.
* @param {...string} blessingsList The blessings to join
-* @param {module:vanadium.security~blessingsCb} cb An optional
-* callback that will return the blessing.
-* @return {Promise<module:vanadium.security~Blessings>} A promise that will
-* be resolved with the blessing.
+* @return {module:vanadium.security~Blessings} A blessing object consisting
+* of the union of the input.
* @memberof module:vanadium.security
*/
-function unionOfBlessings(ctx /*, blessingsA, blessingsB, ..., cb*/) {
- var args = Array.prototype.slice.call(arguments);
- args.shift(); // remove ctx
- var cb;
- if (args.length > 0 && typeof args[args.length - 1] === 'function') {
- cb = args.pop();
- }
- var blessingsList = args;
+function unionOfBlessings(ctx /*, blessingsA, blessingsB, ...*/) {
+ var blessingsList = Array.prototype.slice.call(arguments, 1);
- var runtime = runtimeFromContext(ctx);
- return runtime._controller.unionOfBlessings(ctx, blessingsList, cb);
+ blessingsList = blessingsList.filter(function(blessings) {
+ return !!blessings;
+ });
+
+ switch(blessingsList.length) {
+ case 0:
+ return null;
+ case 1:
+ return blessingsList[0];
+ }
+
+ var firstKey = blessingsList[0].publicKey;
+ var chains = [];
+ for (var i = 0; i < blessingsList.length; i++) {
+ var blessings = blessingsList[i];
+ if (JSON.stringify(blessings.publicKey) !== JSON.stringify(firstKey)) {
+ throw new InvalidUnionError();
+ }
+ chains = chains.concat(blessings.chains);
+ }
+
+ // Sort for prettier and more consistent output.
+ chains = chains.sort(chainSorter);
+
+ return new Blessings({
+ publicKey: firstKey,
+ certificateChains: chains
+ });
+}
+
+// Provide some stability by sorting the chain list.
+// The chains are first ordered by length, followed by the the result
+// of comparing the first differing extension.
+function chainSorter(a, b) {
+ if (a.length !== b.length) {
+ return a.length > b.length;
+ }
+
+ for (var i = 0; i < a.length; i++) {
+ var aext = a[i].extension;
+ var bext = b[i].extension;
+ if (aext !== bext) {
+ return aext > bext;
+ }
+ }
+
+ return false;
}
diff --git a/test/integration/test-blessings-util.js b/test/integration/test-blessings-util.js
index 9a98e0c..48d1a5a 100644
--- a/test/integration/test-blessings-util.js
+++ b/test/integration/test-blessings-util.js
@@ -7,7 +7,21 @@
var config = require('./default-config');
var security = vanadium.security;
-test('Test union of blessings (promise case)', function(t) {
+function validateUnionedBlessings(t, blessings) {
+ t.equal(blessings.chains.length, 2, 'Should have 2 chains');
+ t.equal(blessings.chains[0].length, 1, 'First chain has 1 cert');
+ t.equal(blessings.chains[1].length, 1, 'Second chain has 1 cert');
+ t.equal(blessings.chains[0][0].extension, 'blessedname1',
+ 'Get first extension on first chain');
+ t.equal(blessings.chains[1][0].extension, 'blessedname2',
+ 'Get second extension on second chain');
+ t.deepEqual(blessings.chains[0][0].publicKey, blessings.publicKey,
+ 'First public key matches blessing key');
+ t.deepEqual(blessings.chains[1][0].publicKey, blessings.publicKey,
+ 'Second public key matches blessing key');
+}
+
+test('Test union of blessings', function(t) {
vanadium.init(config, function(err, runtime) {
if (err) {
t.end(err);
@@ -22,10 +36,7 @@
});
})
.then(function(unionedBlessings) {
- t.ok(unionedBlessings.toString().indexOf('blessedname1') >= 0,
- 'first blessing in union');
- t.ok(unionedBlessings.toString().indexOf('blessedname2') >= 0,
- 'second blessing in union');
+ validateUnionedBlessings(t, unionedBlessings);
runtime.close(t.end);
}).catch(function(err) {
runtime.close();
@@ -34,40 +45,31 @@
});
});
-test('Test union of blessings (callback case)', function(t) {
+test('Test union of blessings with differing public keys', function(t) {
vanadium.init(config, function(err, runtime) {
if (err) {
t.end(err);
}
- runtime.principal.blessSelf(runtime.getContext(), 'blessedname1',
- function(err, blessings1) {
- if (err) {
- t.error(err);
- runtime.close(t.end);
- return;
- }
- runtime.principal.blessSelf(runtime.getContext(), 'blessedname2',
- function(err, blessings2) {
- if (err) {
- t.error(err);
- runtime.close(t.end);
- return;
- }
- security.unionOfBlessings(runtime.getContext(), blessings1,
- blessings2, function(err, unionedBlessings) {
- if (err) {
- t.error(err);
- runtime.close(t.end);
- return;
- }
- t.ok(unionedBlessings.toString().indexOf('blessedname1') >= 0,
- 'first blessing in union');
- t.ok(unionedBlessings.toString().indexOf('blessedname2') >= 0,
- 'second blessing in union');
- runtime.close(t.end);
- });
+ runtime.principal.blessSelf(runtime.getContext(), 'blessedname1')
+ .then(function(blessings1) {
+ return runtime.principal.blessSelf(runtime.getContext(), 'blessedname2')
+ .then(function(blessings2) {
+ // modify the public key so it doesn't match
+ blessings2.chains[0][0].publicKey[0] -= 1;
+ return security.unionOfBlessings(
+ runtime.getContext(), blessings1, blessings2);
});
+ })
+ .then(function(unionedBlessings) {
+ runtime.close();
+ t.end('Should have failed due to public keys not matching');
+ }).catch(function(err) {
+ t.ok(err instanceof Error, 'Got error');
+ t.ok(err.toString().indexOf('cannot create union of blessings ' +
+ 'bound to different public keys') !== -1,
+ 'Should get message about keys differing');
+ runtime.close(t.end);
});
});
});