Use filesystem data store to save u2f meta info
parent
9670b23a8b
commit
8b4339f8da
|
@ -10,4 +10,6 @@ COPY src /usr/src
|
|||
ENV PORT=80
|
||||
EXPOSE 80
|
||||
|
||||
VOLUME /var/lib/auth-server
|
||||
|
||||
CMD ["node", "index.js"]
|
||||
|
|
|
@ -3,6 +3,7 @@ version: '2'
|
|||
services:
|
||||
auth:
|
||||
volumes:
|
||||
- ./test:/usr/src/test
|
||||
- ./src/views:/usr/src/views
|
||||
- ./src/public_html:/usr/src/public_html
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ services:
|
|||
- TOTP_SECRET=GRWGIJS6IRHVEODVNRCXCOBMJ5AGC6ZE
|
||||
- SESSION_SECRET=unsecure_secret
|
||||
- SESSION_EXPIRATION_TIME=3600000
|
||||
- STORE_DIRECTORY=/var/lib/auth-server
|
||||
depends_on:
|
||||
- ldap
|
||||
restart: always
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"unit-test": "./node_modules/.bin/mocha --recursive test/unitary",
|
||||
"int-test": "./node_modules/.bin/mocha --recursive test/integration",
|
||||
"all-test": "./node_modules/.bin/mocha --recursive test",
|
||||
"coverage": "./node_modules/.bin/istanbul cover _mocha -- -R spec"
|
||||
"coverage": "./node_modules/.bin/istanbul cover _mocha -- -R spec --recursive test"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -27,6 +27,7 @@
|
|||
"express": "^4.14.0",
|
||||
"express-session": "^1.14.2",
|
||||
"ldapjs": "^1.0.1",
|
||||
"nedb": "^1.8.0",
|
||||
"object-path": "^0.11.3",
|
||||
"speakeasy": "^2.0.0",
|
||||
"winston": "^2.3.1"
|
||||
|
@ -36,6 +37,7 @@
|
|||
"request": "^2.79.0",
|
||||
"should": "^11.1.1",
|
||||
"sinon": "^1.17.6",
|
||||
"sinon-promise": "^0.1.3"
|
||||
"sinon-promise": "^0.1.3",
|
||||
"tmp": "0.0.31"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,8 @@ var config = {
|
|||
ldap_url: process.env.LDAP_URL || 'ldap://127.0.0.1:389',
|
||||
ldap_users_dn: process.env.LDAP_USERS_DN,
|
||||
session_secret: process.env.SESSION_SECRET,
|
||||
session_max_age: process.env.SESSION_MAX_AGE || 3600000 // in ms
|
||||
session_max_age: process.env.SESSION_MAX_AGE || 3600000, // in ms
|
||||
store_directory: process.env.STORE_DIRECTORY
|
||||
}
|
||||
|
||||
var ldap_client = ldap.createClient({
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
|
||||
var user_key_container = {};
|
||||
var denyNotLogged = require('./deny_not_logged');
|
||||
var u2f = require('./u2f')(user_key_container); // create a u2f handler bound to
|
||||
// user key container
|
||||
var u2f = require('./u2f');
|
||||
|
||||
module.exports = {
|
||||
totp: denyNotLogged(require('./totp')),
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
|
||||
module.exports = function(user_key_container) {
|
||||
return {
|
||||
register_request: register_request,
|
||||
register: register(user_key_container),
|
||||
sign_request: sign_request(user_key_container),
|
||||
sign: sign(user_key_container),
|
||||
}
|
||||
module.exports = {
|
||||
register_request: register_request,
|
||||
register: register,
|
||||
sign_request: sign_request,
|
||||
sign: sign,
|
||||
}
|
||||
|
||||
var objectPath = require('object-path');
|
||||
|
@ -26,15 +24,18 @@ function replyWithUnauthorized(res) {
|
|||
res.send();
|
||||
}
|
||||
|
||||
function extractAppId(req) {
|
||||
return util.format('https://%s', req.headers.host);
|
||||
}
|
||||
|
||||
function register_request(req, res) {
|
||||
var u2f = req.app.get('u2f');
|
||||
var logger = req.app.get('logger');
|
||||
var app_id = util.format('https://%s', req.headers.host);
|
||||
var appid = extractAppId(req);
|
||||
|
||||
logger.debug('U2F register_request: headers=%s', JSON.stringify(req.headers));
|
||||
logger.info('U2F register_request: Starting registration');
|
||||
u2f.startRegistration(app_id, [])
|
||||
u2f.startRegistration(appid, [])
|
||||
.then(function(registrationRequest) {
|
||||
logger.info('U2F register_request: Sending back registration request');
|
||||
req.session.auth_session.register_request = registrationRequest;
|
||||
|
@ -46,99 +47,125 @@ function register_request(req, res) {
|
|||
});
|
||||
}
|
||||
|
||||
function register(user_key_container) {
|
||||
return function(req, res) {
|
||||
if(!objectPath.has(req, 'session.auth_session.register_request')) {
|
||||
replyWithUnauthorized(res);
|
||||
return;
|
||||
}
|
||||
|
||||
var u2f = req.app.get('u2f');
|
||||
var registrationRequest = req.session.auth_session.register_request;
|
||||
var logger = req.app.get('logger');
|
||||
|
||||
logger.info('U2F register: Finishing registration');
|
||||
logger.debug('U2F register: register_request=%s', JSON.stringify(registrationRequest));
|
||||
logger.debug('U2F register: body=%s', JSON.stringify(req.body));
|
||||
|
||||
u2f.finishRegistration(registrationRequest, req.body)
|
||||
.then(function(registrationStatus) {
|
||||
logger.info('U2F register: Store registration and reply');
|
||||
var meta = {
|
||||
keyHandle: registrationStatus.keyHandle,
|
||||
publicKey: registrationStatus.publicKey,
|
||||
certificate: registrationStatus.certificate
|
||||
}
|
||||
user_key_container[req.session.auth_session.userid] = meta;
|
||||
res.status(204);
|
||||
res.send();
|
||||
}, function(err) {
|
||||
logger.error('U2F register: %s', err);
|
||||
replyWithInternalError(res, 'Unable to complete the registration');
|
||||
});
|
||||
function register(req, res) {
|
||||
if(!objectPath.has(req, 'session.auth_session.register_request')) {
|
||||
replyWithUnauthorized(res);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function userKeyExists(req, user_key_container) {
|
||||
return req.session.auth_session.userid in user_key_container;
|
||||
}
|
||||
var user_data_storage = req.app.get('user data store');
|
||||
var u2f = req.app.get('u2f');
|
||||
var registrationRequest = req.session.auth_session.register_request;
|
||||
var userid = req.session.auth_session.userid;
|
||||
var appid = extractAppId(req);
|
||||
var logger = req.app.get('logger');
|
||||
|
||||
logger.info('U2F register: Finishing registration');
|
||||
logger.debug('U2F register: register_request=%s', JSON.stringify(registrationRequest));
|
||||
logger.debug('U2F register: body=%s', JSON.stringify(req.body));
|
||||
|
||||
|
||||
function sign_request(user_key_container) {
|
||||
return function(req, res) {
|
||||
if(!userKeyExists(req, user_key_container)) {
|
||||
replyWithMissingRegistration(res);
|
||||
return;
|
||||
u2f.finishRegistration(registrationRequest, req.body)
|
||||
.then(function(registrationStatus) {
|
||||
logger.info('U2F register: Store registration and reply');
|
||||
var meta = {
|
||||
keyHandle: registrationStatus.keyHandle,
|
||||
publicKey: registrationStatus.publicKey,
|
||||
certificate: registrationStatus.certificate
|
||||
}
|
||||
|
||||
var logger = req.app.get('logger');
|
||||
var u2f = req.app.get('u2f');
|
||||
var key = user_key_container[req.session.auth_session.userid];
|
||||
var app_id = util.format('https://%s', req.headers.host);
|
||||
user_data_storage.set_u2f_meta(userid, appid, meta);
|
||||
res.status(204);
|
||||
res.send();
|
||||
}, function(err) {
|
||||
logger.error('U2F register: %s', err);
|
||||
replyWithInternalError(res, 'Unable to complete the registration');
|
||||
});
|
||||
}
|
||||
|
||||
logger.info('U2F sign_request: Start authentication');
|
||||
u2f.startAuthentication(app_id, [key])
|
||||
function retrieveU2fMeta(req, user_data_storage) {
|
||||
var userid = req.session.auth_session.userid;
|
||||
var appid = extractAppId(req);
|
||||
return user_data_storage.get_u2f_meta(userid, appid);
|
||||
}
|
||||
|
||||
function startU2fAuthentication(u2f, appid, meta) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
u2f.startAuthentication(appid, [meta])
|
||||
.then(function(authRequest) {
|
||||
logger.info('U2F sign_request: Store authentication request and reply');
|
||||
req.session.auth_session.sign_request = authRequest;
|
||||
res.status(200);
|
||||
res.json(authRequest);
|
||||
resolve(authRequest);
|
||||
}, function(err) {
|
||||
logger.info('U2F sign_request: %s', err);
|
||||
replyWithUnauthorized(res);
|
||||
reject(err);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function sign(user_key_container) {
|
||||
return function(req, res) {
|
||||
if(!userKeyExists(req, user_key_container)) {
|
||||
function finishU2fAuthentication(u2f, authRequest, data, meta) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
u2f.finishAuthentication(authRequest, data, [meta])
|
||||
.then(function(authenticationStatus) {
|
||||
resolve(authenticationStatus);
|
||||
}, function(err) {
|
||||
reject(err);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function sign_request(req, res) {
|
||||
var logger = req.app.get('logger');
|
||||
var user_data_storage = req.app.get('user data store');
|
||||
|
||||
retrieveU2fMeta(req, user_data_storage)
|
||||
.then(function(doc) {
|
||||
if(!doc) {
|
||||
replyWithMissingRegistration(res);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!objectPath.has(req, 'session.auth_session.sign_request')) {
|
||||
replyWithUnauthorized(res);
|
||||
return;
|
||||
}
|
||||
|
||||
var logger = req.app.get('logger');
|
||||
var u2f = req.app.get('u2f');
|
||||
var meta = doc.meta;
|
||||
var appid = extractAppId(req);
|
||||
logger.info('U2F sign_request: Start authentication');
|
||||
return startU2fAuthentication(u2f, appid, meta);
|
||||
})
|
||||
.then(function(authRequest) {
|
||||
logger.info('U2F sign_request: Store authentication request and reply');
|
||||
req.session.auth_session.sign_request = authRequest;
|
||||
res.status(200);
|
||||
res.json(authRequest);
|
||||
})
|
||||
.catch(function(err) {
|
||||
logger.info('U2F sign_request: %s', err);
|
||||
replyWithUnauthorized(res);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function sign(req, res) {
|
||||
if(!objectPath.has(req, 'session.auth_session.sign_request')) {
|
||||
replyWithUnauthorized(res);
|
||||
return;
|
||||
}
|
||||
|
||||
var logger = req.app.get('logger');
|
||||
var user_data_storage = req.app.get('user data store');
|
||||
|
||||
retrieveU2fMeta(req, user_data_storage)
|
||||
.then(function(doc) {
|
||||
var appid = extractAppId(req);
|
||||
var u2f = req.app.get('u2f');
|
||||
var authRequest = req.session.auth_session.sign_request;
|
||||
var key = user_key_container[req.session.auth_session.userid];
|
||||
|
||||
var meta = doc.meta;
|
||||
logger.info('U2F sign: Finish authentication');
|
||||
u2f.finishAuthentication(authRequest, req.body, [key])
|
||||
.then(function(authenticationStatus) {
|
||||
logger.info('U2F sign: Authentication successful');
|
||||
req.session.auth_session.second_factor = true;
|
||||
res.status(204);
|
||||
res.send();
|
||||
}, function(err) {
|
||||
logger.error('U2F sign: %s', err);
|
||||
res.status(401);
|
||||
res.send();
|
||||
});
|
||||
}
|
||||
return finishU2fAuthentication(u2f, authRequest, req.body, meta);
|
||||
})
|
||||
.then(function(authenticationStatus) {
|
||||
logger.info('U2F sign: Authentication successful');
|
||||
req.session.auth_session.second_factor = true;
|
||||
res.status(204);
|
||||
res.send();
|
||||
}, function(err) {
|
||||
logger.error('U2F sign: %s', err);
|
||||
res.status(401);
|
||||
res.send();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -11,10 +11,14 @@ var speakeasy = require('speakeasy');
|
|||
var path = require('path');
|
||||
var session = require('express-session');
|
||||
var winston = require('winston');
|
||||
var DataStore = require('nedb');
|
||||
var UserDataStore = require('./user_data_store');
|
||||
|
||||
function run(config, ldap_client, u2f, fn) {
|
||||
var view_directory = path.resolve(__dirname, '../views');
|
||||
var public_html_directory = path.resolve(__dirname, '../public_html');
|
||||
var datastore_options = {};
|
||||
datastore_options.directory = config.store_directory;
|
||||
|
||||
var app = express();
|
||||
app.use(express.static(public_html_directory));
|
||||
|
@ -41,6 +45,7 @@ function run(config, ldap_client, u2f, fn) {
|
|||
app.set('ldap client', ldap_client);
|
||||
app.set('totp engine', speakeasy);
|
||||
app.set('u2f', u2f);
|
||||
app.set('user data store', new UserDataStore(DataStore, datastore_options));
|
||||
app.set('config', config);
|
||||
|
||||
app.get ('/login', routes.login);
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
module.exports = UserDataStore;
|
||||
|
||||
var Promise = require('bluebird');
|
||||
var path = require('path');
|
||||
|
||||
function UserDataStore(DataStore, options) {
|
||||
var datastore_options = {};
|
||||
if(options.directory)
|
||||
datastore_options.filename = path.resolve(options.directory, 'u2f_meta');
|
||||
|
||||
datastore_options.inMemoryOnly = options.inMemoryOnly || false;
|
||||
datastore_options.autoload = true;
|
||||
console.log(datastore_options);
|
||||
|
||||
this._u2f_meta_collection = Promise.promisifyAll(new DataStore(datastore_options));
|
||||
}
|
||||
|
||||
UserDataStore.prototype.set_u2f_meta = function(userid, app_id, meta) {
|
||||
var newDocument = {};
|
||||
newDocument.userid = userid;
|
||||
newDocument.appid = app_id;
|
||||
newDocument.meta = meta;
|
||||
|
||||
var filter = {};
|
||||
filter.userid = userid;
|
||||
filter.appid = app_id;
|
||||
|
||||
return this._u2f_meta_collection.updateAsync(filter, newDocument, { upsert: true });
|
||||
}
|
||||
|
||||
UserDataStore.prototype.get_u2f_meta = function(userid, app_id) {
|
||||
var filter = {};
|
||||
filter.userid = userid;
|
||||
filter.appid = app_id;
|
||||
|
||||
return this._u2f_meta_collection.findOneAsync(filter);
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ var winston = require('winston');
|
|||
|
||||
describe('test u2f routes', function() {
|
||||
var req, res;
|
||||
var user_data_store;
|
||||
|
||||
beforeEach(function() {
|
||||
req = {}
|
||||
|
@ -21,8 +22,17 @@ describe('test u2f routes', function() {
|
|||
req.headers = {};
|
||||
req.headers.host = 'localhost';
|
||||
|
||||
var options = {};
|
||||
options.inMemoryOnly = true;
|
||||
|
||||
user_data_store = {};
|
||||
user_data_store.set_u2f_meta = sinon.stub().returns(Promise.resolve({}));
|
||||
user_data_store.get_u2f_meta = sinon.stub().returns(Promise.resolve({}));
|
||||
req.app.get.withArgs('user data store').returns(user_data_store);
|
||||
|
||||
res = {};
|
||||
res.send = sinon.spy();
|
||||
res.json = sinon.spy();
|
||||
res.status = sinon.spy();
|
||||
})
|
||||
|
||||
|
@ -31,8 +41,6 @@ describe('test u2f routes', function() {
|
|||
describe('test signing request', test_signing_request);
|
||||
describe('test signing', test_signing);
|
||||
|
||||
|
||||
|
||||
function test_registration_request() {
|
||||
it('should send back the registration request and save it in the session', function(done) {
|
||||
var expectedRequest = {
|
||||
|
@ -49,7 +57,7 @@ describe('test u2f routes', function() {
|
|||
u2f_mock.startRegistration.returns(Promise.resolve(expectedRequest));
|
||||
|
||||
req.app.get.withArgs('u2f').returns(u2f_mock);
|
||||
u2f(user_key_container).register_request(req, res);
|
||||
u2f.register_request(req, res);
|
||||
});
|
||||
|
||||
it('should return internal error on registration request', function(done) {
|
||||
|
@ -63,21 +71,19 @@ describe('test u2f routes', function() {
|
|||
u2f_mock.startRegistration.returns(Promise.reject('Internal error'));
|
||||
|
||||
req.app.get.withArgs('u2f').returns(u2f_mock);
|
||||
u2f(user_key_container).register_request(req, res);
|
||||
u2f.register_request(req, res);
|
||||
});
|
||||
}
|
||||
|
||||
function test_registration() {
|
||||
it('should return status code 200', function(done) {
|
||||
var user_key_container = {};
|
||||
it('should save u2f meta and return status code 200', function(done) {
|
||||
var expectedStatus = {
|
||||
keyHandle: 'keyHandle',
|
||||
publicKey: 'pbk',
|
||||
certificate: 'cert'
|
||||
};
|
||||
res.send = sinon.spy(function(data) {
|
||||
assert('user' in user_key_container);
|
||||
assert.deepEqual(expectedStatus, user_key_container['user']);
|
||||
assert('user', user_data_store.set_u2f_meta.getCall(0).args[0])
|
||||
done();
|
||||
});
|
||||
var u2f_mock = {};
|
||||
|
@ -86,7 +92,7 @@ describe('test u2f routes', function() {
|
|||
|
||||
req.session.auth_session.register_request = {};
|
||||
req.app.get.withArgs('u2f').returns(u2f_mock);
|
||||
u2f(user_key_container).register(req, res);
|
||||
u2f.register(req, res);
|
||||
});
|
||||
|
||||
it('should return unauthorized error on registration request', function(done) {
|
||||
|
@ -100,7 +106,7 @@ describe('test u2f routes', function() {
|
|||
u2f_mock.finishRegistration.returns(Promise.reject('Internal error'));
|
||||
|
||||
req.app.get.withArgs('u2f').returns(u2f_mock);
|
||||
u2f(user_key_container).register(req, res);
|
||||
u2f.register(req, res);
|
||||
});
|
||||
|
||||
it('should return unauthorized error when no auth request has been initiated', function(done) {
|
||||
|
@ -114,7 +120,7 @@ describe('test u2f routes', function() {
|
|||
u2f_mock.finishRegistration.returns(Promise.resolve());
|
||||
|
||||
req.app.get.withArgs('u2f').returns(u2f_mock);
|
||||
u2f(user_key_container).register(req, res);
|
||||
u2f.register(req, res);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -136,7 +142,7 @@ describe('test u2f routes', function() {
|
|||
u2f_mock.startAuthentication.returns(Promise.resolve(expectedRequest));
|
||||
|
||||
req.app.get.withArgs('u2f').returns(u2f_mock);
|
||||
u2f(user_key_container).sign_request(req, res);
|
||||
u2f.sign_request(req, res);
|
||||
});
|
||||
|
||||
it('should return unauthorized error on registration request error', function(done) {
|
||||
|
@ -151,7 +157,7 @@ describe('test u2f routes', function() {
|
|||
u2f_mock.startAuthentication.returns(Promise.reject('Internal error'));
|
||||
|
||||
req.app.get.withArgs('u2f').returns(u2f_mock);
|
||||
u2f(user_key_container).sign_request(req, res);
|
||||
u2f.sign_request(req, res);
|
||||
});
|
||||
|
||||
it('should send unauthorized error when no registration exists', function(done) {
|
||||
|
@ -167,8 +173,13 @@ describe('test u2f routes', function() {
|
|||
u2f_mock.startAuthentication = sinon.stub();
|
||||
u2f_mock.startAuthentication.returns(Promise.resolve(expectedRequest));
|
||||
|
||||
user_data_store.get_u2f_meta = sinon.stub().returns(Promise.resolve());
|
||||
|
||||
req.app.get = sinon.stub();
|
||||
req.app.get.withArgs('logger').returns(winston);
|
||||
req.app.get.withArgs('user data store').returns(user_data_store);
|
||||
req.app.get.withArgs('u2f').returns(u2f_mock);
|
||||
u2f(user_key_container).sign_request(req, res);
|
||||
u2f.sign_request(req, res);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -192,7 +203,7 @@ describe('test u2f routes', function() {
|
|||
|
||||
req.session.auth_session.sign_request = {};
|
||||
req.app.get.withArgs('u2f').returns(u2f_mock);
|
||||
u2f(user_key_container).sign(req, res);
|
||||
u2f.sign(req, res);
|
||||
});
|
||||
|
||||
it('should return unauthorized error on registration request internal error', function(done) {
|
||||
|
@ -209,7 +220,7 @@ describe('test u2f routes', function() {
|
|||
|
||||
req.session.auth_session.sign_request = {};
|
||||
req.app.get.withArgs('u2f').returns(u2f_mock);
|
||||
u2f(user_key_container).register(req, res);
|
||||
u2f.register(req, res);
|
||||
});
|
||||
|
||||
it('should return unauthorized error when no sign request has been initiated', function(done) {
|
||||
|
@ -223,7 +234,7 @@ describe('test u2f routes', function() {
|
|||
u2f_mock.finishAuthentication.returns(Promise.resolve());
|
||||
|
||||
req.app.get.withArgs('u2f').returns(u2f_mock);
|
||||
u2f(user_key_container).register(req, res);
|
||||
u2f.register(req, res);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -0,0 +1,169 @@
|
|||
|
||||
var server = require('../../src/lib/server');
|
||||
|
||||
var request = require('request');
|
||||
var assert = require('assert');
|
||||
var speakeasy = require('speakeasy');
|
||||
var sinon = require('sinon');
|
||||
var Promise = require('bluebird');
|
||||
var tmp = require('tmp');
|
||||
|
||||
var request = Promise.promisifyAll(request);
|
||||
|
||||
var PORT = 8050;
|
||||
var BASE_URL = 'http://localhost:' + PORT;
|
||||
|
||||
describe('test data persistence', function() {
|
||||
var u2f;
|
||||
var tmpDir;
|
||||
var ldap_client = {
|
||||
bind: sinon.stub()
|
||||
};
|
||||
var config;
|
||||
|
||||
before(function() {
|
||||
u2f = {};
|
||||
u2f.startRegistration = sinon.stub();
|
||||
u2f.finishRegistration = sinon.stub();
|
||||
u2f.startAuthentication = sinon.stub();
|
||||
u2f.finishAuthentication = sinon.stub();
|
||||
|
||||
ldap_client.bind.withArgs('cn=test_ok,ou=users,dc=example,dc=com',
|
||||
'password').yields(undefined);
|
||||
ldap_client.bind.withArgs('cn=test_nok,ou=users,dc=example,dc=com',
|
||||
'password').yields('error');
|
||||
tmpDir = tmp.dirSync({ unsafeCleanup: true });
|
||||
config = {
|
||||
port: PORT,
|
||||
totp_secret: 'totp_secret',
|
||||
ldap_url: 'ldap://127.0.0.1:389',
|
||||
ldap_users_dn: 'ou=users,dc=example,dc=com',
|
||||
session_secret: 'session_secret',
|
||||
session_max_age: 50000,
|
||||
store_directory: tmpDir.name
|
||||
};
|
||||
});
|
||||
|
||||
after(function() {
|
||||
tmpDir.removeCallback();
|
||||
});
|
||||
|
||||
it('should save a u2f meta and reload it after a restart of the server', function() {
|
||||
var server;
|
||||
var sign_request = {};
|
||||
var sign_status = {};
|
||||
var registration_request = {};
|
||||
var registration_status = {};
|
||||
u2f.startRegistration.returns(Promise.resolve(sign_request));
|
||||
u2f.finishRegistration.returns(Promise.resolve(sign_status));
|
||||
u2f.startAuthentication.returns(Promise.resolve(registration_request));
|
||||
u2f.finishAuthentication.returns(Promise.resolve(registration_status));
|
||||
|
||||
var j1 = request.jar();
|
||||
var j2 = request.jar();
|
||||
return start_server(config, ldap_client, u2f)
|
||||
.then(function(s) {
|
||||
server = s;
|
||||
return execute_login(j1);
|
||||
})
|
||||
.then(function(res) {
|
||||
return execute_first_factor(j1);
|
||||
})
|
||||
.then(function() {
|
||||
return execute_u2f_registration(j1);
|
||||
})
|
||||
.then(function() {
|
||||
return execute_u2f_authentication(j1);
|
||||
})
|
||||
.then(function() {
|
||||
return stop_server(server);
|
||||
})
|
||||
.then(function() {
|
||||
return start_server(config, ldap_client, u2f)
|
||||
})
|
||||
.then(function(s) {
|
||||
server = s;
|
||||
return execute_login(j2);
|
||||
})
|
||||
.then(function() {
|
||||
return execute_first_factor(j2);
|
||||
})
|
||||
.then(function() {
|
||||
return execute_u2f_authentication(j2);
|
||||
})
|
||||
.then(function(res) {
|
||||
assert.equal(204, res.statusCode);
|
||||
server.close();
|
||||
return Promise.resolve();
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.error(err);
|
||||
return Promise.reject(err);
|
||||
});
|
||||
});
|
||||
|
||||
function start_server(config, ldap_client, u2f) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var s = server.run(config, ldap_client, u2f);
|
||||
resolve(s);
|
||||
});
|
||||
}
|
||||
|
||||
function stop_server(s) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
s.close();
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function execute_first_factor(jar) {
|
||||
return request.postAsync({
|
||||
url: BASE_URL + '/_auth/1stfactor',
|
||||
jar: jar,
|
||||
form: {
|
||||
username: 'test_ok',
|
||||
password: 'password'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function execute_u2f_registration(jar) {
|
||||
return request.getAsync({
|
||||
url: BASE_URL + '/_auth/2ndfactor/u2f/register_request',
|
||||
jar: jar
|
||||
})
|
||||
.then(function(res) {
|
||||
return request.postAsync({
|
||||
url: BASE_URL + '/_auth/2ndfactor/u2f/register',
|
||||
jar: jar,
|
||||
form: {
|
||||
s: 'test'
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function execute_u2f_authentication(jar) {
|
||||
return request.getAsync({
|
||||
url: BASE_URL + '/_auth/2ndfactor/u2f/sign_request',
|
||||
jar: jar
|
||||
})
|
||||
.then(function() {
|
||||
return request.postAsync({
|
||||
url: BASE_URL + '/_auth/2ndfactor/u2f/sign',
|
||||
jar: jar,
|
||||
form: {
|
||||
s: 'test'
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function execute_verification(jar) {
|
||||
return request.getAsync({ url: BASE_URL + '/_verify', jar: jar })
|
||||
}
|
||||
|
||||
function execute_login(jar) {
|
||||
return request.getAsync({ url: BASE_URL + '/login', jar: jar })
|
||||
}
|
||||
});
|
|
@ -0,0 +1,68 @@
|
|||
|
||||
var UserDataStore = require('../../src/lib/user_data_store');
|
||||
var DataStore = require('nedb');
|
||||
var assert = require('assert');
|
||||
var Promise = require('bluebird');
|
||||
|
||||
describe('test user data store', function() {
|
||||
it('should save a u2f meta', function() {
|
||||
var options = {};
|
||||
options.inMemoryOnly = true;
|
||||
|
||||
var data_store = new UserDataStore(DataStore, options);
|
||||
|
||||
var userid = 'user';
|
||||
var app_id = 'https://localhost';
|
||||
var meta = {};
|
||||
meta.publicKey = 'pbk';
|
||||
|
||||
return data_store.set_u2f_meta(userid, app_id, meta)
|
||||
.then(function(numUpdated) {
|
||||
assert.equal(1, numUpdated);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
it('should retrieve no u2f meta', function() {
|
||||
var options = {};
|
||||
options.inMemoryOnly = true;
|
||||
|
||||
var data_store = new UserDataStore(DataStore, options);
|
||||
|
||||
var userid = 'user';
|
||||
var app_id = 'https://localhost';
|
||||
var meta = {};
|
||||
meta.publicKey = 'pbk';
|
||||
|
||||
return data_store.get_u2f_meta(userid, app_id)
|
||||
.then(function(doc) {
|
||||
assert.equal(undefined, doc);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
it('should insert and retrieve a u2f meta', function() {
|
||||
var options = {};
|
||||
options.inMemoryOnly = true;
|
||||
|
||||
var data_store = new UserDataStore(DataStore, options);
|
||||
|
||||
var userid = 'user';
|
||||
var app_id = 'https://localhost';
|
||||
var meta = {};
|
||||
meta.publicKey = 'pbk';
|
||||
|
||||
return data_store.set_u2f_meta(userid, app_id, meta)
|
||||
.then(function(numUpdated, data) {
|
||||
assert.equal(1, numUpdated);
|
||||
return data_store.get_u2f_meta(userid, app_id)
|
||||
})
|
||||
.then(function(doc) {
|
||||
assert.deepEqual(meta, doc.meta);
|
||||
assert.deepEqual(userid, doc.userid);
|
||||
assert.deepEqual(app_id, doc.appid);
|
||||
assert('_id' in doc);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue