1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Allow adminAuth.user to be a Function

Fixes #1461
This commit is contained in:
Nick O'Leary 2018-01-23 23:08:11 +00:00
parent cc9011cd68
commit 3cb5cbd8d5
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
4 changed files with 97 additions and 91 deletions

View File

@ -14,8 +14,6 @@
* limitations under the License. * limitations under the License.
**/ **/
var when = require("when");
var clients = [ var clients = [
{id:"node-red-editor",secret:"not_available"}, {id:"node-red-editor",secret:"not_available"},
{id:"node-red-admin",secret:"not_available"} {id:"node-red-admin",secret:"not_available"}
@ -25,9 +23,9 @@ module.exports = {
get: function(id) { get: function(id) {
for (var i=0;i<clients.length;i++) { for (var i=0;i<clients.length;i++) {
if (clients[i].id == id) { if (clients[i].id == id) {
return when.resolve(clients[i]); return Promise.resolve(clients[i]);
} }
} }
return when.resolve(null); return Promise.resolve(null);
} }
} }

View File

@ -135,6 +135,66 @@ function completeVerify(profile,done) {
}); });
} }
function genericStrategy(adminApp,strategy) {
var crypto = require("crypto")
var session = require('express-session')
var MemoryStore = require('memorystore')(session)
adminApp.use(session({
// As the session is only used across the life-span of an auth
// hand-shake, we can use a instance specific random string
secret: crypto.randomBytes(20).toString('hex'),
resave: false,
saveUninitialized: false,
store: new MemoryStore({
checkPeriod: 86400000 // prune expired entries every 24h
})
}));
//TODO: all passport references ought to be in ./auth
adminApp.use(passport.initialize());
adminApp.use(passport.session());
var options = strategy.options;
passport.use(new strategy.strategy(options,
function() {
var originalDone = arguments[arguments.length-1];
if (options.verify) {
var args = Array.from(arguments);
args[args.length-1] = function(err,profile) {
if (err) {
return originalDone(err);
} else {
return completeVerify(profile,originalDone);
}
};
options.verify.apply(null,args);
} else {
var profile = arguments[arguments.length - 2];
return completeVerify(profile,originalDone);
}
}
));
adminApp.get('/auth/strategy',
passport.authenticate(strategy.name, {session:false, failureRedirect: settings.httpAdminRoot }),
completeGenerateStrategyAuth
);
adminApp.get('/auth/strategy/callback',
passport.authenticate(strategy.name, {session:false, failureRedirect: settings.httpAdminRoot }),
completeGenerateStrategyAuth
);
}
function completeGenerateStrategyAuth(req,res) {
var tokens = req.user.tokens;
delete req.user.tokens;
// Successful authentication, redirect home.
res.redirect(settings.httpAdminRoot + '?access_token='+tokens.accessToken);
}
module.exports = { module.exports = {
init: init, init: init,
needsPermission: needsPermission, needsPermission: needsPermission,
@ -149,58 +209,5 @@ module.exports = {
}, },
login: login, login: login,
revoke: revoke, revoke: revoke,
genericStrategy: function(adminApp,strategy) { genericStrategy: genericStrategy
var crypto = require("crypto")
var session = require('express-session')
var MemoryStore = require('memorystore')(session)
adminApp.use(session({
// As the session is only used across the life-span of an auth
// hand-shake, we can use a instance specific random string
secret: crypto.randomBytes(20).toString('hex'),
resave: false,
saveUninitialized: false,
store: new MemoryStore({
checkPeriod: 86400000 // prune expired entries every 24h
})
}));
//TODO: all passport references ought to be in ./auth
adminApp.use(passport.initialize());
adminApp.use(passport.session());
var options = strategy.options;
passport.use(new strategy.strategy(options,
function() {
var originalDone = arguments[arguments.length-1];
if (options.verify) {
var args = Array.prototype.slice.call(arguments);
args[args.length-1] = function(err,profile) {
if (err) {
return originalDone(err);
} else {
return completeVerify(profile,originalDone);
}
};
options.verify.apply(null,args);
} else {
var profile = arguments[arguments.length - 2];
return completeVerify(profile,originalDone);
}
}
));
adminApp.get('/auth/strategy', passport.authenticate(strategy.name));
adminApp.get('/auth/strategy/callback',
passport.authenticate(strategy.name, {session:false, failureRedirect: settings.httpAdminRoot }),
function(req, res) {
var tokens = req.user.tokens;
delete req.user.tokens;
// Successful authentication, redirect home.
res.redirect(settings.httpAdminRoot + '?access_token='+tokens.accessToken);
}
);
}
} }

View File

@ -14,8 +14,6 @@
* limitations under the License. * limitations under the License.
**/ **/
var when = require("when");
function generateToken(length) { function generateToken(length) {
var c = "ABCDEFGHIJKLMNOPQRSTUZWXYZabcdefghijklmnopqrstuvwxyz1234567890"; var c = "ABCDEFGHIJKLMNOPQRSTUZWXYZabcdefghijklmnopqrstuvwxyz1234567890";
var token = []; var token = [];
@ -49,7 +47,7 @@ function expireSessions() {
if (modified) { if (modified) {
return storage.saveSessions(sessions); return storage.saveSessions(sessions);
} else { } else {
return when.resolve(); return Promise.resolve();
} }
} }
function loadSessions() { function loadSessions() {
@ -69,7 +67,7 @@ module.exports = {
// At this point, storage will not have been initialised, so defer loading // At this point, storage will not have been initialised, so defer loading
// the sessions until there's a request for them. // the sessions until there's a request for them.
loadedSessions = null; loadedSessions = null;
return when.resolve(); return Promise.resolve();
}, },
get: function(token) { get: function(token) {
return loadSessions().then(function() { return loadSessions().then(function() {
@ -78,7 +76,7 @@ module.exports = {
return expireSessions().then(function() { return null }); return expireSessions().then(function() { return null });
} }
} }
return when.resolve(sessions[token]); return Promise.resolve(sessions[token]);
}); });
}, },
create: function(user,client,scope) { create: function(user,client,scope) {

View File

@ -14,13 +14,12 @@
* limitations under the License. * limitations under the License.
**/ **/
var when = require("when");
var util = require("util"); var util = require("util");
var clone = require("clone");
var bcrypt; var bcrypt;
try { bcrypt = require('bcrypt'); } try { bcrypt = require('bcrypt'); }
catch(e) { bcrypt = require('bcryptjs'); } catch(e) { bcrypt = require('bcryptjs'); }
var users = {}; var users = {};
var passwords = {};
var defaultUser = null; var defaultUser = null;
function authenticate() { function authenticate() {
@ -28,31 +27,33 @@ function authenticate() {
if (typeof username !== 'string') { if (typeof username !== 'string') {
username = username.username; username = username.username;
} }
var user = users[username]; const args = Array.from(arguments);
if (user) { return api.get(username).then(function(user) {
if (arguments.length === 2) { if (user) {
// Username/password authentication if (args.length === 2) {
var password = arguments[1]; // Username/password authentication
return when.promise(function(resolve,reject) { var password = args[1];
bcrypt.compare(password, passwords[username], function(err, res) { return new Promise(function(resolve,reject) {
resolve(res?user:null); bcrypt.compare(password, user.password, function(err, res) {
resolve(res?cleanUser(user):null);
});
}); });
}); } else {
} else { // Try to extract common profile information
// Try to extract common profile information if (args[0].hasOwnProperty('photos') && args[0].photos.length > 0) {
if (arguments[0].hasOwnProperty('photos') && arguments[0].photos.length > 0) { user.image = args[0].photos[0].value;
user.image = arguments[0].photos[0].value; }
return cleanUser(user);
} }
return when.resolve(user);
} }
} return null;
return when.resolve(null); });
} }
function get(username) { function get(username) {
return when.resolve(users[username]); return Promise.resolve(users[username]);
} }
function getDefaultUser() { function getDefaultUser() {
return when.resolve(null); return Promise.resolve(null);
} }
var api = { var api = {
@ -63,7 +64,6 @@ var api = {
function init(config) { function init(config) {
users = {}; users = {};
passwords = {};
defaultUser = null; defaultUser = null;
if (config.type == "credentials" || config.type == "strategy") { if (config.type == "credentials" || config.type == "strategy") {
if (config.users) { if (config.users) {
@ -77,11 +77,7 @@ function init(config) {
} }
for (var i=0;i<us.length;i++) { for (var i=0;i<us.length;i++) {
var u = us[i]; var u = us[i];
users[u.username] = { users[u.username] = clone(u);
"username":u.username,
"permissions":u.permissions
};
passwords[u.username] = u.password;
} }
} }
} }
@ -96,7 +92,7 @@ function init(config) {
api.default = config.default; api.default = config.default;
} else { } else {
api.default = function() { api.default = function() {
return when.resolve({ return Promise.resolve({
"anonymous": true, "anonymous": true,
"permissions":config.default.permissions "permissions":config.default.permissions
}); });
@ -106,10 +102,17 @@ function init(config) {
api.default = getDefaultUser; api.default = getDefaultUser;
} }
} }
function cleanUser(user) {
if (user && user.hasOwnProperty('password')) {
user = clone(user);
delete user.password;
}
return user;
}
module.exports = { module.exports = {
init: init, init: init,
get: function(username) { return api.get(username) }, get: function(username) { return api.get(username).then(cleanUser)},
authenticate: function() { return api.authenticate.apply(null, arguments) }, authenticate: function() { return api.authenticate.apply(null, arguments) },
default: function() { return api.default(); } default: function() { return api.default(); }
}; };