mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Tie auth middleware to needsPermission api
This commit is contained in:
parent
3ef6f29d6e
commit
c31ffb98b0
@ -23,6 +23,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "3.17.2",
|
"express": "3.17.2",
|
||||||
"when": "3.4.6",
|
"when": "3.4.6",
|
||||||
|
"bcryptjs": "2.1.0",
|
||||||
"nopt": "3.0.1",
|
"nopt": "3.0.1",
|
||||||
"mqtt": "0.3.x",
|
"mqtt": "0.3.x",
|
||||||
"ws": "0.4.32",
|
"ws": "0.4.32",
|
||||||
|
@ -20,8 +20,9 @@ var oauth2orize = require("oauth2orize");
|
|||||||
var strategies = require("./strategies");
|
var strategies = require("./strategies");
|
||||||
var Tokens = require("./tokens");
|
var Tokens = require("./tokens");
|
||||||
var Users = require("./users");
|
var Users = require("./users");
|
||||||
|
var permissions = require("./permissions");
|
||||||
|
|
||||||
var settings = require("../../settings");
|
var settings = null;
|
||||||
var log = require("../../log");
|
var log = require("../../log");
|
||||||
|
|
||||||
|
|
||||||
@ -33,22 +34,29 @@ var server = oauth2orize.createServer();
|
|||||||
|
|
||||||
server.exchange(oauth2orize.exchange.password(strategies.passwordTokenExchange));
|
server.exchange(oauth2orize.exchange.password(strategies.passwordTokenExchange));
|
||||||
|
|
||||||
function init() {
|
function init(_settings) {
|
||||||
|
settings = _settings;
|
||||||
if (settings.adminAuth) {
|
if (settings.adminAuth) {
|
||||||
Users.init(settings.adminAuth);
|
Users.init(settings.adminAuth);
|
||||||
Tokens.init(settings)
|
Tokens.init(settings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function authenticate(req,res,next) {
|
function needsPermission(permission) {
|
||||||
if (settings.adminAuth) {
|
return function(req,res,next) {
|
||||||
if (/^\/auth\/.*/.test(req.originalUrl)) {
|
if (settings.adminAuth) {
|
||||||
next();
|
return passport.authenticate(['bearer','anon'],{ session: false })(req,res,function() {
|
||||||
|
if (!req.user) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
if (permissions.hasPermission(req.user,permission)) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
return res.send(401);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
return passport.authenticate(['bearer','anon'], { session: false })(req,res,next);
|
next();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
next();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +91,7 @@ function revoke(req,res) {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
init: init,
|
init: init,
|
||||||
authenticate: authenticate,
|
needsPermission: needsPermission,
|
||||||
ensureClientSecret: ensureClientSecret,
|
ensureClientSecret: ensureClientSecret,
|
||||||
authenticateClient: authenticateClient,
|
authenticateClient: authenticateClient,
|
||||||
getToken: getToken,
|
getToken: getToken,
|
||||||
|
@ -19,18 +19,6 @@ var util = require('util');
|
|||||||
var readRE = /^((.+)\.)?read$/
|
var readRE = /^((.+)\.)?read$/
|
||||||
var writeRE = /^((.+)\.)?write$/
|
var writeRE = /^((.+)\.)?write$/
|
||||||
|
|
||||||
function needsPermission(perm) {
|
|
||||||
return function(req,res,next) {
|
|
||||||
if (!req.user) {
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
if (hasPermission(req.user,perm)) {
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
return res.send(401);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasPermission(user,permission) {
|
function hasPermission(user,permission) {
|
||||||
if (!user.permissions) {
|
if (!user.permissions) {
|
||||||
return false;
|
return false;
|
||||||
@ -45,6 +33,4 @@ function hasPermission(user,permission) {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
hasPermission: hasPermission,
|
hasPermission: hasPermission,
|
||||||
needsPermission: needsPermission,
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,30 +15,8 @@
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
var when = require("when");
|
var when = require("when");
|
||||||
var crypto = require("crypto");
|
|
||||||
var util = require("util");
|
var util = require("util");
|
||||||
/*
|
var bcrypt = require('bcryptjs');
|
||||||
adminAuth: {
|
|
||||||
type: "credentials",
|
|
||||||
users: [{
|
|
||||||
username: "nol",
|
|
||||||
password: "5f4dcc3b5aa765d61d8327deb882cf99" // password
|
|
||||||
permissions: "* read write"
|
|
||||||
}],
|
|
||||||
default: {
|
|
||||||
permissions: "* read write"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
adminAuth: {
|
|
||||||
type: "credentials",
|
|
||||||
users: function(username) {return when.resolve(user)},
|
|
||||||
authenticate: function(username,password) { return when.resolve(user);}
|
|
||||||
default: function() { return when.resolve(defaultUser) }
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
//{username:"nick",password:crypto.createHash('md5').update("foo",'utf8').digest('hex')}
|
|
||||||
|
|
||||||
var users = {};
|
var users = {};
|
||||||
var passwords = {};
|
var passwords = {};
|
||||||
@ -47,10 +25,11 @@ var defaultUser = null;
|
|||||||
function authenticate(username,password) {
|
function authenticate(username,password) {
|
||||||
var user = users[username];
|
var user = users[username];
|
||||||
if (user) {
|
if (user) {
|
||||||
var pass = crypto.createHash('md5').update(password,'utf8').digest('hex');
|
return when.promise(function(resolve,reject) {
|
||||||
if (pass == passwords[username]) {
|
bcrypt.compare(password, passwords[username], function(err, res) {
|
||||||
return when.resolve(user);
|
resolve(res?user:null);
|
||||||
}
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return when.resolve(null);
|
return when.resolve(null);
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ var library = require("./library");
|
|||||||
var info = require("./info");
|
var info = require("./info");
|
||||||
|
|
||||||
var auth = require("./auth");
|
var auth = require("./auth");
|
||||||
var needsPermission = require("./auth/permissions").needsPermission;
|
var needsPermission = auth.needsPermission;
|
||||||
|
|
||||||
var settings = require("../settings");
|
var settings = require("../settings");
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ var errorHandler = function(err,req,res,next) {
|
|||||||
|
|
||||||
function init(adminApp) {
|
function init(adminApp) {
|
||||||
|
|
||||||
auth.init();
|
auth.init(settings);
|
||||||
|
|
||||||
// Editor
|
// Editor
|
||||||
if (!settings.disableEditor) {
|
if (!settings.disableEditor) {
|
||||||
@ -55,7 +55,6 @@ function init(adminApp) {
|
|||||||
if (settings.adminAuth) {
|
if (settings.adminAuth) {
|
||||||
//TODO: all passport references ought to be in ./auth
|
//TODO: all passport references ought to be in ./auth
|
||||||
adminApp.use(passport.initialize());
|
adminApp.use(passport.initialize());
|
||||||
adminApp.use(auth.authenticate);
|
|
||||||
adminApp.post("/auth/token",
|
adminApp.post("/auth/token",
|
||||||
auth.ensureClientSecret,
|
auth.ensureClientSecret,
|
||||||
auth.authenticateClient,
|
auth.authenticateClient,
|
||||||
|
@ -17,10 +17,11 @@
|
|||||||
var redApp = null;
|
var redApp = null;
|
||||||
var storage = require("../storage");
|
var storage = require("../storage");
|
||||||
var log = require("../log");
|
var log = require("../log");
|
||||||
|
var needsPermission = require("./auth").needsPermission;
|
||||||
|
|
||||||
function createLibrary(type) {
|
function createLibrary(type) {
|
||||||
if (redApp) {
|
if (redApp) {
|
||||||
redApp.get(new RegExp("/library/"+type+"($|\/(.*))"),function(req,res) {
|
redApp.get(new RegExp("/library/"+type+"($|\/(.*))"),needsPermission("library.read"),function(req,res) {
|
||||||
var path = req.params[1]||"";
|
var path = req.params[1]||"";
|
||||||
storage.getLibraryEntry(type,path).then(function(result) {
|
storage.getLibraryEntry(type,path).then(function(result) {
|
||||||
if (typeof result === "string") {
|
if (typeof result === "string") {
|
||||||
@ -42,7 +43,7 @@ function createLibrary(type) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
redApp.post(new RegExp("/library/"+type+"\/(.*)"),function(req,res) {
|
redApp.post(new RegExp("/library/"+type+"\/(.*)"),needsPermission("library.write"),function(req,res) {
|
||||||
var path = req.params[0];
|
var path = req.params[0];
|
||||||
var fullBody = '';
|
var fullBody = '';
|
||||||
req.on('data', function(chunk) {
|
req.on('data', function(chunk) {
|
||||||
|
@ -23,7 +23,7 @@ var util = require("./util");
|
|||||||
var fs = require("fs");
|
var fs = require("fs");
|
||||||
var settings = require("./settings");
|
var settings = require("./settings");
|
||||||
var credentials = require("./nodes/credentials");
|
var credentials = require("./nodes/credentials");
|
||||||
var permissions = require("./api/auth/permissions");
|
var auth = require("./api/auth");
|
||||||
|
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ var RED = {
|
|||||||
settings:settings,
|
settings:settings,
|
||||||
util: util,
|
util: util,
|
||||||
auth: {
|
auth: {
|
||||||
needsPermission: permissions.needsPermission
|
needsPermission: auth.needsPermission
|
||||||
},
|
},
|
||||||
version: function () {
|
version: function () {
|
||||||
var p = require(path.join(process.env.NODE_RED_HOME,"package.json"));
|
var p = require(path.join(process.env.NODE_RED_HOME,"package.json"));
|
||||||
|
@ -27,59 +27,6 @@ var settings = require("../../../../red/settings");
|
|||||||
|
|
||||||
|
|
||||||
describe("api auth middleware",function() {
|
describe("api auth middleware",function() {
|
||||||
describe("authenticate",function() {
|
|
||||||
it("does not trigger on auth paths", sinon.test(function(done) {
|
|
||||||
this.stub(passport,"authenticate",function() {
|
|
||||||
return function() {
|
|
||||||
settings.reset();
|
|
||||||
done(new Error("authentication not applied to auth path"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
settings.init({adminAuth:{}});
|
|
||||||
var req = {
|
|
||||||
originalUrl: "/auth/token"
|
|
||||||
};
|
|
||||||
auth.authenticate(req,null,function() {
|
|
||||||
settings.reset();
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
}));
|
|
||||||
it("does trigger on non-auth paths", sinon.test(function(done) {
|
|
||||||
this.stub(passport,"authenticate",function() {
|
|
||||||
return function() {
|
|
||||||
settings.reset();
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
settings.init({adminAuth:{}});
|
|
||||||
var req = {
|
|
||||||
originalUrl: "/"
|
|
||||||
};
|
|
||||||
auth.authenticate(req,null,function() {
|
|
||||||
settings.reset();
|
|
||||||
done(new Error("authentication applied to non-auth path"));
|
|
||||||
});
|
|
||||||
|
|
||||||
}));
|
|
||||||
it("does not trigger on non-auth paths with auth disabled", sinon.test(function(done) {
|
|
||||||
this.stub(passport,"authenticate",function() {
|
|
||||||
return function() {
|
|
||||||
settings.reset();
|
|
||||||
done(new Error("authentication applied when disabled"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
settings.init({});
|
|
||||||
var req = {
|
|
||||||
originalUrl: "/"
|
|
||||||
};
|
|
||||||
auth.authenticate(req,null,function() {
|
|
||||||
settings.reset();
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("ensureClientSecret", function() {
|
describe("ensureClientSecret", function() {
|
||||||
it("leaves client_secret alone if not present",function(done) {
|
it("leaves client_secret alone if not present",function(done) {
|
||||||
|
@ -35,27 +35,4 @@ describe("Auth permissions", function() {
|
|||||||
permissions.hasPermission({permissions:"read"},"node.write").should.be.false;
|
permissions.hasPermission({permissions:"read"},"node.write").should.be.false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("needsPermission middleware", function() {
|
|
||||||
it('passes if no user on request',function(done) {
|
|
||||||
var needsPermission = permissions.needsPermission("*");
|
|
||||||
needsPermission({},null,function() {
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('passes if user has required permission',function(done) {
|
|
||||||
var needsPermission = permissions.needsPermission("read");
|
|
||||||
needsPermission({user:{permissions:"read"}},null,function() {
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('rejects if user does not have required permission',function(done) {
|
|
||||||
var needsPermission = permissions.needsPermission("write");
|
|
||||||
needsPermission({user:{permissions:"read"}},{send: function(code) {
|
|
||||||
code.should.equal(401);
|
|
||||||
done();
|
|
||||||
}},null);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -27,7 +27,8 @@ describe("Users", function() {
|
|||||||
type:"credentials",
|
type:"credentials",
|
||||||
users:[{
|
users:[{
|
||||||
username:"fred",
|
username:"fred",
|
||||||
password:"5f4dcc3b5aa765d61d8327deb882cf99", // 'password'
|
password:'$2a$08$LpYMefvGZ3MjAfZGzcoyR.1BcfHh4wy4NpbN.cEny5aHnWOqjKOXK',
|
||||||
|
// 'password' -> require('bcryptjs').hashSync('password', 8);
|
||||||
permissions:"*"
|
permissions:"*"
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
|
@ -24,6 +24,7 @@ var app = express();
|
|||||||
var RED = require("../../../red/red.js");
|
var RED = require("../../../red/red.js");
|
||||||
var storage = require("../../../red/storage");
|
var storage = require("../../../red/storage");
|
||||||
var library = require("../../../red/api/library");
|
var library = require("../../../red/api/library");
|
||||||
|
var auth = require("../../../red/api/auth");
|
||||||
|
|
||||||
describe("library api", function() {
|
describe("library api", function() {
|
||||||
|
|
||||||
@ -166,6 +167,7 @@ describe("library api", function() {
|
|||||||
app = express();
|
app = express();
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
library.init(app);
|
library.init(app);
|
||||||
|
auth.init({});
|
||||||
RED.library.register("test");
|
RED.library.register("test");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user