Skip to content
This repository has been archived by the owner on Oct 6, 2022. It is now read-only.

Adding multiple backends support #200

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# 3.0
- Added:
- A new required option `dataSource` in the config - to support multiple backends
- A new required option `loadBalancerFile` in the config - which is a path to the Load Balancer file.
- Additional backends are now supported - made possible by refactoring & wrapping the etcD support
- Changed:
- Add a failure flash when Azure gives you Auth problems to match the Google Auth experience

---
2 changes: 2 additions & 0 deletions config/config.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
{
"RequiresAuth": false,
"dataSource": "etcd",
"etcdHost": "127.0.0.1",
"etcdPort": "4001",
"hobknobHost": "localhost",
"hobknobPort": "3006",
"loadBalancerFile": "/etc/lbstatus/hobknob",
"categories": [
{
"id": 0,
Expand Down
4 changes: 2 additions & 2 deletions server/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ var auditRoutes = require('./routes/auditRoutes');
var applicationRoutes = require('./routes/applicationRoutes');
var featureRoutes = require('./routes/featureRoutes');
var path = require('path');
var acl = require('./acl');
var acl = require('./domain/acl');
var config = require('./../config/config.json');
var _ = require('underscore');
var passport = require('./auth').init(config);
Expand Down Expand Up @@ -119,7 +119,7 @@ app.get('/auth/azureadoauth2',
);

app.get('/auth/azureadoauth2/callback',
passport.authenticate('azure', { failureRedirect: '/oops' }),
passport.authenticate('azure', { failureRedirect: '/oops', failureFlash: true }),
function (req, res) {
// Successful authentication, redirect home.
res.redirect('/');
Expand Down
34 changes: 34 additions & 0 deletions server/domain/acl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use strict';

var config = require('./../../config/config.json');
var acl = function() {
switch (config.dataSource.toLowerCase()) {
case 'etcd':
return require('./etcd/acl');

default:
return null;
}
};

module.exports = {
grant: function (userEmail, resource, callback) {
acl().grant(userEmail, resource, callback);
},

assert: function (userEmail, resource, callback) {
acl().assert(userEmail, resource, callback);
},

revoke: function (userEmail, resource, callback) {
acl().revoke(userEmail, resource, callback);
},

revokeAll: function(resource, callback) {
acl().revokeAll(resource, callback);
},

getAllUsers: function (resource, callback) {
acl().getAllUsers(resource, callback);
}
};
116 changes: 14 additions & 102 deletions server/domain/application.js
Original file line number Diff line number Diff line change
@@ -1,126 +1,38 @@
'use strict';

var etcd = require('../etcd');
var _ = require('underscore');
var config = require('./../../config/config.json');
var acl = require('./../acl');
var audit = require('./../audit');
var etcdBaseUrl = 'http://' + config.etcdHost + ':' + config.etcdPort + '/v2/keys/';

var getUserDetails = function (req) {
return config.RequiresAuth ? req.user._json : {name: 'Anonymous'};
var application = function() {
switch (config.dataSource.toLowerCase()) {
case 'etcd':
return require('./etcd/application');

default:
return null;
}
};

module.exports = {
getApplications: function (cb) {
etcd.client.get('v1/toggles/', {recursive: false}, function (err, result) {
if (err) {
if (err.errorCode === 100) { // key not found
return cb(null, []);
}

return cb(err);
}

var applications = _.map(result.node.nodes || [], function (node) {
var splitKey = node.key.split('/');
return splitKey[splitKey.length - 1];
});
cb(null, applications);
});
application().getApplications(cb);
},

addApplication: function (applicationName, req, cb) {
var path = 'v1/toggles/' + applicationName;
etcd.client.mkdir(path, function (err) {
if (err) {
return cb(err);
}

audit.addApplicationAudit(getUserDetails(req), applicationName, 'Created', function () {
if (err) {
console.log(err); // todo: better logging
}
});

// todo: not sure if this is correct
if (config.RequiresAuth) {
var userEmail = getUserDetails(req).email.toLowerCase(); // todo: need better user management
acl.grant(userEmail, applicationName, function (grantErr) {
if (grantErr) {
cb(grantErr);
return;
}
cb();
});
} else {
cb();
}
});
application().addApplication(applicationName, req, cb);
},

deleteApplication: function (applicationName, req, cb) {
var path = 'v1/toggles/' + applicationName;
etcd.client.delete(path, {recursive: true}, function (err) {
if (err) {
return cb(err);
}

audit.addApplicationAudit(getUserDetails(req), applicationName, 'Deleted', function () {
if (err) {
console.log(err);
}
});

if (config.RequiresAuth) {
acl.revokeAll(applicationName, function (revokeErr) {
if (revokeErr) {
return cb(revokeErr);
}
cb();
});
} else {
cb();
}
});
application().deleteApplication(applicationName, req, cb);
},

getApplicationMetaData: function (applicationName, cb) {
etcd.client.get('v1/metadata/' + applicationName, {recursive: true}, function (err, result) {
if (err) {
if (err.errorCode === 100) { // key not found
cb(null, {});
} else {
cb(err);
}
return;
}
var metaDataKeyValues = _.map(result.node.nodes, function (subNode) {
var metaDataKey = _.last(subNode.key.split('/'));
return [metaDataKey, subNode.value];
});
cb(null, _.object(metaDataKeyValues));
});
application().getApplicationMetaData(applicationName, cb);
},

deleteApplicationMetaData: function (applicationName, cb) {
etcd.client.delete('v1/metadata/' + applicationName, {recursive: true}, function (err, result) {
if (err) {
if (err.errorCode === 100) { // key not found
cb();
} else {
cb(err);
}
return;
}
cb();
});
application().deleteApplicationMetaData(applicationName, cb);
},

saveApplicationMetaData: function (applicationName, metaDataKey, metaDataValue, cb) {
var path = 'v1/metadata/' + applicationName + '/' + metaDataKey;
etcd.client.set(path, metaDataValue, function (err) {
return cb(err);
});
application().saveApplicationMetaData(applicationName, metaDataKey, metaDataValue, cb);
}
};
30 changes: 30 additions & 0 deletions server/domain/audit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use strict';

var config = require('./../../config/config.json');
var audit = function() {
switch (config.dataSource.toLowerCase()) {
case 'etcd':
return require('./etcd/audit');

default:
return null;
}
};

module.exports = {
getApplicationAuditTrail: function (applicationName, callback) {
audit().getApplicationAuditTrail(applicationName, callback);
},

getFeatureAuditTrail: function (applicationName, featureName, callback) {
audit().getFeatureAuditTrail(applicationName, featureName, callback);
},

addApplicationAudit: function (user, applicationName, action, callback) {
audit().addApplicationAudit(user, applicationName, action, callback);
},

addFeatureAudit: function (user, applicationName, featureName, toggleName, value, action, callback) {
audit().addFeatureAudit(user, applicationName, featureName, toggleName, value, action, callback);
}
};
6 changes: 2 additions & 4 deletions server/domain/category.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

var _ = require('underscore');
var config = require('./../../config/config.json');
var acl = require('./../acl');
var audit = require('./../audit');
var etcdBaseUrl = 'http://' + config.etcdHost + ':' + config.etcdPort + '/v2/keys/';
var acl = require('./acl');
var audit = require('./audit');

var getCategory = function (id, name, description, columns, features) {
return {
Expand Down Expand Up @@ -41,4 +40,3 @@ module.exports.getCategoriesFromConfig = function () {
});
return _.object(categories);
};

Loading