Create a filesystem notifier for simple getting started
parent
7e41c68aa7
commit
d29aac78d0
|
@ -1,20 +1,33 @@
|
|||
|
||||
debug_level: info
|
||||
### Level of verbosity for logs
|
||||
logs_level: info
|
||||
|
||||
### Configuration of your LDAP
|
||||
ldap:
|
||||
url: ldap://ldap
|
||||
base_dn: ou=users,dc=example,dc=com
|
||||
user: cn=admin,dc=example,dc=com
|
||||
password: password
|
||||
|
||||
### Configuration of session cookies
|
||||
session:
|
||||
secret: unsecure_secret
|
||||
expiration: 3600000
|
||||
|
||||
store_directory: /var/lib/auth-server
|
||||
### The directory where the DB files will be saved
|
||||
store_directory: /var/lib/auth-server/store
|
||||
|
||||
|
||||
### Notifications are sent to users when they require a password reset, a u2f
|
||||
### registration or a TOTP registration.
|
||||
### Use only one available configuration: filesystem, gmail
|
||||
notifier:
|
||||
gmail:
|
||||
username: user@example.com
|
||||
password: yourpassword
|
||||
### For testing purpose, notifications can be sent in a file
|
||||
filesystem:
|
||||
filename: /var/lib/auth-server/notifications/notification.txt
|
||||
|
||||
### Use your gmail account to send the notifications. You can use an app password.
|
||||
# gmail:
|
||||
# username: user@example.com
|
||||
# password: yourpassword
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ services:
|
|||
- ./test:/usr/src/test
|
||||
- ./src/views:/usr/src/views
|
||||
- ./src/public_html:/usr/src/public_html
|
||||
- ./notifications:/var/lib/auth-server/notifications
|
||||
|
||||
ldap-admin:
|
||||
image: osixia/phpldapadmin:0.6.11
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
User: user
|
||||
Subject: Reset your password
|
||||
Link: https://localhost:8080/authentication/reset-password?identity_token=CmJ51IdJLEcVr7AbbJPANe0wmJoOcgYzPqgGOngVRIhKq1UbQUoS44FXDEXBcolz
|
|
@ -23,13 +23,12 @@ var config = {
|
|||
session_secret: yaml_config.session.secret,
|
||||
session_max_age: yaml_config.session.expiration || 3600000, // in ms
|
||||
store_directory: yaml_config.store_directory,
|
||||
debug_level: yaml_config.debug_level,
|
||||
gmail: {
|
||||
user: yaml_config.notifier.gmail.username,
|
||||
pass: yaml_config.notifier.gmail.password
|
||||
}
|
||||
logs_level: yaml_config.logs_level,
|
||||
notifier: yaml_config.notifier,
|
||||
}
|
||||
|
||||
console.log(config);
|
||||
|
||||
var ldap_client = ldap.createClient({
|
||||
url: config.ldap_url,
|
||||
reconnect: true
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
|
||||
module.exports = EmailSender;
|
||||
|
||||
var Promise = require('bluebird');
|
||||
|
||||
function EmailSender(nodemailer, options) {
|
||||
var transporter = nodemailer.createTransport({
|
||||
service: 'gmail',
|
||||
auth: {
|
||||
user: options.gmail.user,
|
||||
pass: options.gmail.pass
|
||||
}
|
||||
});
|
||||
this.transporter = Promise.promisifyAll(transporter);
|
||||
}
|
||||
|
||||
EmailSender.prototype.send = function(to, subject, html) {
|
||||
var mailOptions = {};
|
||||
mailOptions.from = 'auth-server@open-intent.io';
|
||||
mailOptions.to = to;
|
||||
mailOptions.subject = subject;
|
||||
mailOptions.html = html;
|
||||
return this.transporter.sendMailAsync(mailOptions);
|
||||
}
|
||||
|
|
@ -14,13 +14,12 @@ var email_template = fs.readFileSync(filePath, 'utf8');
|
|||
|
||||
// IdentityCheck class
|
||||
|
||||
function IdentityCheck(user_data_store, email_sender, logger) {
|
||||
function IdentityCheck(user_data_store, logger) {
|
||||
this._user_data_store = user_data_store;
|
||||
this._email_sender = email_sender;
|
||||
this._logger = logger;
|
||||
}
|
||||
|
||||
IdentityCheck.prototype.issue_token = function(userid, email, content, logger) {
|
||||
IdentityCheck.prototype.issue_token = function(userid, content, logger) {
|
||||
var five_minutes = 4 * 60 * 1000;
|
||||
var token = randomstring.generate({ length: 64 });
|
||||
var that = this;
|
||||
|
@ -61,7 +60,7 @@ function identity_check_get(endpoint, icheck_interface) {
|
|||
|
||||
var email_sender = req.app.get('email sender');
|
||||
var user_data_store = req.app.get('user data store');
|
||||
var identity_check = new IdentityCheck(user_data_store, email_sender, logger);
|
||||
var identity_check = new IdentityCheck(user_data_store, logger);
|
||||
|
||||
identity_check.consume_token(identity_token, logger)
|
||||
.then(function(content) {
|
||||
|
@ -90,15 +89,16 @@ function identity_check_get(endpoint, icheck_interface) {
|
|||
function identity_check_post(endpoint, icheck_interface) {
|
||||
return function(req, res) {
|
||||
var logger = req.app.get('logger');
|
||||
var email_sender = req.app.get('email sender');
|
||||
var notifier = req.app.get('notifier');
|
||||
var user_data_store = req.app.get('user data store');
|
||||
var identity_check = new IdentityCheck(user_data_store, email_sender, logger);
|
||||
var userid, email_address;
|
||||
var identity_check = new IdentityCheck(user_data_store, logger);
|
||||
var identity;
|
||||
|
||||
icheck_interface.pre_check_callback(req)
|
||||
.then(function(identity) {
|
||||
email_address = objectPath.get(identity, 'email');
|
||||
userid = objectPath.get(identity, 'userid');
|
||||
.then(function(id) {
|
||||
identity = id;
|
||||
var email_address = objectPath.get(identity, 'email');
|
||||
var userid = objectPath.get(identity, 'userid');
|
||||
|
||||
if(!(email_address && userid)) {
|
||||
throw new exceptions.IdentityError('Missing user id or email address');
|
||||
|
@ -111,19 +111,9 @@ function identity_check_post(endpoint, icheck_interface) {
|
|||
.then(function(token) {
|
||||
var original_url = util.format('https://%s%s', req.headers.host, req.headers['x-original-uri']);
|
||||
var link_url = util.format('%s?identity_token=%s', original_url, token);
|
||||
var email = {};
|
||||
|
||||
var d = {};
|
||||
d.url = link_url;
|
||||
d.button_title = 'Continue';
|
||||
d.title = icheck_interface.email_subject;
|
||||
|
||||
email.to = email_address;
|
||||
email.subject = icheck_interface.email_subject;
|
||||
email.content = ejs.render(email_template, d);
|
||||
|
||||
logger.info('POST identity_check: send email to %s', email.to);
|
||||
return email_sender.send(email.to, email.subject, email.content);
|
||||
logger.info('POST identity_check: notify to %s', identity.userid);
|
||||
return notifier.notify(identity, icheck_interface.email_subject, link_url);
|
||||
})
|
||||
.then(function() {
|
||||
res.status(204);
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
|
||||
module.exports = Notifier;
|
||||
|
||||
var GmailNotifier = require('./notifiers/gmail.js');
|
||||
var FSNotifier = require('./notifiers/filesystem.js');
|
||||
|
||||
function notifier_factory(options, deps) {
|
||||
if('gmail' in options) {
|
||||
return new GmailNotifier(options.gmail, deps);
|
||||
}
|
||||
else if('filesystem' in options) {
|
||||
return new FSNotifier(options.filesystem);
|
||||
}
|
||||
}
|
||||
|
||||
function Notifier(options, deps) {
|
||||
this._notifier = notifier_factory(options, deps);
|
||||
}
|
||||
|
||||
Notifier.prototype.notify = function(identity, subject, link) {
|
||||
return this._notifier.notify(identity, subject, link);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
module.exports = FSNotifier;
|
||||
|
||||
var Promise = require('bluebird');
|
||||
var fs = Promise.promisifyAll(require('fs'));
|
||||
var util = require('util');
|
||||
|
||||
function FSNotifier(options) {
|
||||
this._filename = options.filename;
|
||||
}
|
||||
|
||||
FSNotifier.prototype.notify = function(identity, subject, link) {
|
||||
var content = util.format('User: %s\nSubject: %s\nLink: %s', identity.userid,
|
||||
subject, link);
|
||||
return fs.writeFileAsync(this._filename, content);
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
module.exports = GmailNotifier;
|
||||
|
||||
var Promise = require('bluebird');
|
||||
var fs = require('fs');
|
||||
var ejs = require('ejs');
|
||||
|
||||
var email_template = fs.readFileSync(__dirname + '/../../resources/email-template.ejs', 'UTF-8');
|
||||
|
||||
function GmailNotifier(options, deps) {
|
||||
var transporter = deps.nodemailer.createTransport({
|
||||
service: 'gmail',
|
||||
auth: {
|
||||
user: options.username,
|
||||
pass: options.password
|
||||
}
|
||||
});
|
||||
this.transporter = Promise.promisifyAll(transporter);
|
||||
}
|
||||
|
||||
GmailNotifier.prototype.notify = function(identity, subject, link) {
|
||||
var d = {};
|
||||
d.url = link;
|
||||
d.button_title = 'Continue';
|
||||
d.title = subject;
|
||||
|
||||
var mailOptions = {};
|
||||
mailOptions.from = 'auth-server@open-intent.io';
|
||||
mailOptions.to = identity.email;
|
||||
mailOptions.subject = subject;
|
||||
mailOptions.html = ejs.render(email_template, d);
|
||||
return this.transporter.sendMailAsync(mailOptions);
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ var path = require('path');
|
|||
var session = require('express-session');
|
||||
var winston = require('winston');
|
||||
var UserDataStore = require('./user_data_store');
|
||||
var EmailSender = require('./email_sender');
|
||||
var Notifier = require('./notifier');
|
||||
var AuthenticationRegulator = require('./authentication_regulator');
|
||||
var identity_check = require('./identity_check');
|
||||
|
||||
|
@ -24,9 +24,6 @@ function run(config, ldap_client, deps, fn) {
|
|||
if(config.store_in_memory)
|
||||
datastore_options.inMemory = true;
|
||||
|
||||
var email_options = {};
|
||||
email_options.gmail = config.gmail;
|
||||
|
||||
var app = express();
|
||||
app.use(express.static(public_html_directory));
|
||||
app.use(bodyParser.urlencoded({ extended: false }));
|
||||
|
@ -46,12 +43,13 @@ function run(config, ldap_client, deps, fn) {
|
|||
app.set('views', view_directory);
|
||||
app.set('view engine', 'ejs');
|
||||
|
||||
winston.level = config.debug_level || 'info';
|
||||
// by default the level of logs is info
|
||||
winston.level = config.logs_level || 'info';
|
||||
|
||||
var five_minutes = 5 * 60;
|
||||
var data_store = new UserDataStore(deps.nedb, datastore_options);
|
||||
var regulator = new AuthenticationRegulator(data_store, five_minutes);
|
||||
var notifier = new EmailSender(deps.nodemailer, email_options);
|
||||
var notifier = new Notifier(config.notifier, deps);
|
||||
|
||||
app.set('logger', winston);
|
||||
app.set('ldap', deps.ldap);
|
||||
|
@ -59,7 +57,7 @@ function run(config, ldap_client, deps, fn) {
|
|||
app.set('totp engine', speakeasy);
|
||||
app.set('u2f', deps.u2f);
|
||||
app.set('user data store', data_store);
|
||||
app.set('email sender', notifier);
|
||||
app.set('notifier', notifier);
|
||||
app.set('authentication regulator', regulator);
|
||||
app.set('config', config);
|
||||
|
||||
|
@ -88,9 +86,11 @@ function run(config, ldap_client, deps, fn) {
|
|||
app.post (base_endpoint + '/1stfactor', routes.first_factor);
|
||||
app.post (base_endpoint + '/2ndfactor/totp', routes.second_factor.totp);
|
||||
|
||||
// U2F registration
|
||||
app.get (base_endpoint + '/2ndfactor/u2f/register_request', routes.second_factor.u2f.register_request);
|
||||
app.post (base_endpoint + '/2ndfactor/u2f/register', routes.second_factor.u2f.register);
|
||||
|
||||
// U2F authentication
|
||||
app.get (base_endpoint + '/2ndfactor/u2f/sign_request', routes.second_factor.u2f.sign_request);
|
||||
app.post (base_endpoint + '/2ndfactor/u2f/sign', routes.second_factor.u2f.sign);
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
var sinon = require('sinon');
|
||||
var assert = require('assert');
|
||||
var FSNotifier = require('../../../src/lib/notifiers/filesystem');
|
||||
var tmp = require('tmp');
|
||||
var fs = require('fs');
|
||||
|
||||
describe('test FS notifier', function() {
|
||||
var tmpDir;
|
||||
before(function() {
|
||||
tmpDir = tmp.dirSync({ unsafeCleanup: true });
|
||||
});
|
||||
|
||||
after(function() {
|
||||
tmpDir.removeCallback();
|
||||
});
|
||||
|
||||
it('should write the notification in a file', function() {
|
||||
var options = {};
|
||||
options.filename = tmpDir.name + '/notification';
|
||||
|
||||
var sender = new FSNotifier(options);
|
||||
var subject = 'subject';
|
||||
|
||||
var identity = {};
|
||||
identity.userid = 'user';
|
||||
identity.email = 'user@example.com';
|
||||
|
||||
var url = 'http://test.com';
|
||||
|
||||
return sender.notify(identity, subject, url)
|
||||
.then(function() {
|
||||
var content = fs.readFileSync(options.filename, 'UTF-8');
|
||||
assert(content.length > 0);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,36 @@
|
|||
var sinon = require('sinon');
|
||||
var assert = require('assert');
|
||||
var GmailNotifier = require('../../../src/lib/notifiers/gmail');
|
||||
|
||||
describe('test gmail notifier', function() {
|
||||
it('should send an email', function() {
|
||||
var nodemailer = {};
|
||||
var transporter = {};
|
||||
nodemailer.createTransport = sinon.stub().returns(transporter);
|
||||
transporter.sendMail = sinon.stub().yields();
|
||||
var options = {};
|
||||
options.username = 'user_gmail';
|
||||
options.password = 'pass_gmail';
|
||||
|
||||
var deps = {};
|
||||
deps.nodemailer = nodemailer;
|
||||
|
||||
var sender = new GmailNotifier(options, deps);
|
||||
var subject = 'subject';
|
||||
|
||||
var identity = {};
|
||||
identity.userid = 'user';
|
||||
identity.email = 'user@example.com';
|
||||
|
||||
var url = 'http://test.com';
|
||||
|
||||
return sender.notify(identity, subject, url)
|
||||
.then(function() {
|
||||
assert.equal(nodemailer.createTransport.getCall(0).args[0].auth.user, 'user_gmail');
|
||||
assert.equal(nodemailer.createTransport.getCall(0).args[0].auth.pass, 'pass_gmail');
|
||||
assert.equal(transporter.sendMail.getCall(0).args[0].to, 'user@example.com');
|
||||
assert.equal(transporter.sendMail.getCall(0).args[0].subject, 'subject');
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,35 @@
|
|||
|
||||
var sinon = require('sinon');
|
||||
var Promise = require('bluebird');
|
||||
var assert = require('assert');
|
||||
|
||||
var Notifier = require('../../../src/lib/notifier');
|
||||
var GmailNotifier = require('../../../src/lib/notifiers/gmail');
|
||||
var FSNotifier = require('../../../src/lib/notifiers/filesystem');
|
||||
|
||||
describe('test notifier', function() {
|
||||
it('should build a Gmail Notifier', function() {
|
||||
var deps = {};
|
||||
deps.nodemailer = {};
|
||||
deps.nodemailer.createTransport = sinon.stub().returns({});
|
||||
|
||||
var options = {};
|
||||
options.gmail = {};
|
||||
options.gmail.user = 'abc';
|
||||
options.gmail.pass = 'abcd';
|
||||
|
||||
var notifier = new Notifier(options, deps);
|
||||
assert(notifier._notifier instanceof GmailNotifier);
|
||||
});
|
||||
|
||||
it('should build a FS Notifier', function() {
|
||||
var deps = {};
|
||||
|
||||
var options = {};
|
||||
options.filesystem = {};
|
||||
options.filesystem.filename = 'abc';
|
||||
|
||||
var notifier = new Notifier(options, deps);
|
||||
assert(notifier._notifier instanceof FSNotifier);
|
||||
});
|
||||
});
|
|
@ -57,7 +57,7 @@ describe('test data persistence', function() {
|
|||
session_secret: 'session_secret',
|
||||
session_max_age: 50000,
|
||||
store_directory: tmpDir.name,
|
||||
gmail: { user: 'user@example.com', pass: 'password' }
|
||||
notifier: { gmail: { user: 'user@example.com', pass: 'password' } }
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
|
||||
|
||||
var sinon = require('sinon');
|
||||
var assert = require('assert');
|
||||
var EmailSender = require('../../src/lib/email_sender');
|
||||
|
||||
describe('test email sender', function() {
|
||||
it('should send an email', function() {
|
||||
var nodemailer = {};
|
||||
var transporter = {};
|
||||
nodemailer.createTransport = sinon.stub().returns(transporter);
|
||||
transporter.sendMail = sinon.stub().yields();
|
||||
var options = {};
|
||||
options.gmail = {};
|
||||
options.gmail.user = 'test@gmail.com';
|
||||
options.gmail.pass = 'test@gmail.com';
|
||||
|
||||
var sender = new EmailSender(nodemailer, options);
|
||||
var to = 'example@gmail.com';
|
||||
var subject = 'subject';
|
||||
var content = 'content';
|
||||
|
||||
return sender.send(to, subject, content)
|
||||
.then(function() {
|
||||
assert.equal(to, transporter.sendMail.getCall(0).args[0].to);
|
||||
assert.equal(subject, transporter.sendMail.getCall(0).args[0].subject);
|
||||
assert.equal(content, transporter.sendMail.getCall(0).args[0].html);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -9,7 +9,7 @@ var Promise = require('bluebird');
|
|||
describe('test identity check process', function() {
|
||||
var req, res, app, icheck_interface;
|
||||
var user_data_store;
|
||||
var email_sender;
|
||||
var notifier;
|
||||
|
||||
beforeEach(function() {
|
||||
req = {};
|
||||
|
@ -25,9 +25,8 @@ describe('test identity check process', function() {
|
|||
user_data_store.consume_identity_check_token = sinon.stub();
|
||||
user_data_store.consume_identity_check_token.returns(Promise.resolve({ userid: 'user' }));
|
||||
|
||||
email_sender = {};
|
||||
email_sender.send = sinon.stub();
|
||||
email_sender.send = sinon.stub().returns(Promise.resolve());
|
||||
notifier = {};
|
||||
notifier.notify = sinon.stub().returns(Promise.resolve());
|
||||
|
||||
req.headers = {};
|
||||
req.session = {};
|
||||
|
@ -38,7 +37,7 @@ describe('test identity check process', function() {
|
|||
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('email sender').returns(email_sender);
|
||||
req.app.get.withArgs('notifier').returns(notifier);
|
||||
|
||||
res.status = sinon.spy();
|
||||
res.send = sinon.spy();
|
||||
|
@ -126,7 +125,7 @@ describe('test identity check process', function() {
|
|||
|
||||
res.send = sinon.spy(function() {
|
||||
assert.equal(res.status.getCall(0).args[0], 204);
|
||||
assert(email_sender.send.calledOnce);
|
||||
assert(notifier.notify.calledOnce);
|
||||
assert(user_data_store.issue_identity_check_token.calledOnce);
|
||||
assert.equal(user_data_store.issue_identity_check_token.getCall(0).args[0], 'user');
|
||||
assert.equal(user_data_store.issue_identity_check_token.getCall(0).args[3], 240000);
|
||||
|
|
|
@ -38,10 +38,12 @@ describe('test the server', function() {
|
|||
session_secret: 'session_secret',
|
||||
session_max_age: 50000,
|
||||
store_in_memory: true,
|
||||
notifier: {
|
||||
gmail: {
|
||||
user: 'user@example.com',
|
||||
pass: 'password'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
u2f = {};
|
||||
|
|
Loading…
Reference in New Issue