blob: b8ff575716cf85ffcec69ba768a1f502d48df0f7 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
var vanadium = require('../..');
var test = require('prova');
var Deferred = require('../../src/lib/deferred');
var serve = require('./serve');
var access = vanadium.security.access;
var permissionsAuthorizer = vanadium.security.access.permissionsAuthorizer;
var allowEveryoneAuthorizer = vanadium.security.access.allowEveryoneAuthorizer;
var service = {
call: function(ctx, serverCall, arg) {
return Promise.resolve(1);
}
};
function createPromiseDispatcher(authorizer, tags) {
function auth(ctx, call) {
if (call.method === '__Signature') {
return null;
}
return authorizer(ctx, call);
}
var desc = {
methods: [
{
name: 'Call',
tags: tags
}
]
};
service._serviceDescription = desc;
return function callbackDispatcher(suffix) {
return {
service: service,
authorizer: auth,
};
};
}
function testErrorCase(assert, authorizer, tags, isPromise) {
serve({
name: 'authorizerTestService',
autoBind: false,
dispatcher: isPromise ?
createPromiseDispatcher(authorizer, tags) :
createCallbackDispatcher(authorizer, tags)
}, function(err, res) {
if (err) {
return assert.end(err);
}
var client = res.runtime.newClient();
var ctx = res.runtime.getContext();
client.bindTo(ctx, 'authorizerTestService/auth').then(function(service) {
service.call(ctx, 'foo').then(function(value) {
assert.error(new Error('call should not have succeeded. res:' + value));
res.end(assert);
}).catch(function(err) {
assert.ok(err);
res.end(assert);
});
}, function(err) {
assert.error(err);
res.end(assert);
});
});
}
test('Test errors are properly returned',
function(assert) {
testErrorCase(assert, function (ctx, call) {
var def = new Deferred();
function reject() {
def.reject(new Error('unauthorized'));
}
setTimeout(reject, 0);
return def.promise;
}, [], true);
});
function createCallbackDispatcher(authorizer, tags) {
function auth(ctx, call, cb) {
if (call.method === '__Signature') {
cb(null);
}
authorizer(ctx, call, cb);
}
var desc = {
methods: [
{
name: 'Call',
tags: tags
}
]
};
service._serviceDescription = desc;
return function callbackDispatcher(suffix) {
return {
service: service,
authorizer: auth,
};
};
}
function testSuccessCase(assert, authorizer, tags, isPromise) {
serve({
name: 'authorizerTestService',
autoBind: false,
dispatcher: isPromise ?
createPromiseDispatcher(authorizer, tags) :
createCallbackDispatcher(authorizer, tags)
}, function(err, res) {
if (err) {
return assert.end(err);
}
var client = res.runtime.newClient();
var ctx = res.runtime.getContext();
client.bindTo(ctx, 'authorizerTestService/auth').then(function(service) {
return service.call(ctx, 'foo');
}).then(function() {
assert.pass('RPC resolved');
res.end(assert);
}).catch(function(err) {
assert.fail('RPC rejected with error:' + err.toString());
res.end(assert);
});
});
}
test('Test successes are handled', function(assert) {
testSuccessCase(assert, function (ctx, call, cb) {
process.nextTick(cb.bind(null, null));
});
});
test('Test proper context is passed to authorizer', function(assert) {
var defaultBlessingRegex = require('./default-blessing-regex');
testSuccessCase(assert, function (ctx, call, cb) {
if (!defaultBlessingRegex.test(call.remoteBlessingStrings[0])) {
return cb(new Error('unknown remote blessings ' +
call.remoteBlessingStrings));
} else if (!defaultBlessingRegex.test(call.localBlessingStrings[0])) {
cb(new Error('unknown local blessings ' + call.localBlessingStrings));
} else if (call.method !== 'call') {
return cb(new Error('wrong method ' + call.method));
} else if (call.suffix !== 'auth') {
return cb(new Error('wrong suffix ' + call.suffix));
}
// TODO(bjornick): Fix the endpoint format
// } else if (call.remoteEndpoint === endpoint ||
// !call.remoteEndpoint) {
// return new Error('bad endpoint ' + call.remoteEndpoint);
// } else if (call.localEndpoint !== endpoint ||
// !call.localEndpoint) {
// return new Error('bad endpoint ' + call.localEndpoint);
// }
cb();
});
});
test('Test passing in labels', function(assert) {
testSuccessCase(assert, function(ctx, call, cb) {
if (call.methodTags[0] !== 'foo') {
return cb(new Error('wrong label ' + call.label));
}
return cb();
}, ['foo']);
});
var perms = {
foo: {
in: ['...']
},
bar: {}
};
var tagPermsAuthorizer = permissionsAuthorizer(perms, access.Tag);
test('Test permissionsAuthorizer (public key) - success', function(assert) {
var tagBar = new access.Tag('Bar');
// Passes because we reuse the same public key, despite the tag not being ok.
testSuccessCase(assert, tagPermsAuthorizer, [tagBar], true);
});
test('Test permissionsAuthorizer (tag) - success', function(assert) {
var tagFoo = new access.Tag('Foo');
function diffPublicKeyPermsAuthorizer(ctx, call) {
// get rid of public key, just for this example
call.remoteBlessings.publicKey = '';
tagPermsAuthorizer(ctx, call);
}
// Everyone is allowed via the Foo tag.
testSuccessCase(assert, diffPublicKeyPermsAuthorizer, [tagFoo], true);
});
test('Test permissionsAuthorizer (tag) - failure', function(assert) {
var tagBar = new access.Tag('Bar');
function diffPublicKeyPermsAuthorizer(ctx, call) {
// get rid of public key, just for this example to demonstrate failure
call.remoteBlessings.publicKey = '';
tagPermsAuthorizer(ctx, call);
}
// Nobody is allowed via the Bar tag.
testErrorCase(assert, diffPublicKeyPermsAuthorizer, [tagBar], true);
});
test('Test allowEveryoneAuthorizer() - success', function(assert) {
testSuccessCase(assert, allowEveryoneAuthorizer(), [], true);
});