mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Merge branch 'dev' into pr_1789
This commit is contained in:
213
packages/node_modules/@node-red/editor-api/lib/auth/index.js
vendored
Normal file
213
packages/node_modules/@node-red/editor-api/lib/auth/index.js
vendored
Normal file
@@ -0,0 +1,213 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var passport = require("passport");
|
||||
var oauth2orize = require("oauth2orize");
|
||||
|
||||
var strategies = require("./strategies");
|
||||
var Tokens = require("./tokens");
|
||||
var Users = require("./users");
|
||||
var permissions = require("./permissions");
|
||||
|
||||
var theme = require("../editor/theme");
|
||||
|
||||
var settings = null;
|
||||
var log = require("@node-red/util").log; // TODO: separate module
|
||||
|
||||
|
||||
passport.use(strategies.bearerStrategy.BearerStrategy);
|
||||
passport.use(strategies.clientPasswordStrategy.ClientPasswordStrategy);
|
||||
passport.use(strategies.anonymousStrategy);
|
||||
|
||||
var server = oauth2orize.createServer();
|
||||
|
||||
server.exchange(oauth2orize.exchange.password(strategies.passwordTokenExchange));
|
||||
|
||||
function init(_settings,storage) {
|
||||
settings = _settings;
|
||||
if (settings.adminAuth) {
|
||||
var mergedAdminAuth = Object.assign({}, settings.adminAuth, settings.adminAuth.module);
|
||||
Users.init(mergedAdminAuth);
|
||||
Tokens.init(mergedAdminAuth,runtime.storage);
|
||||
}
|
||||
}
|
||||
|
||||
function needsPermission(permission) {
|
||||
return function(req,res,next) {
|
||||
if (settings && settings.adminAuth) {
|
||||
return passport.authenticate(['bearer','anon'],{ session: false })(req,res,function() {
|
||||
if (!req.user) {
|
||||
return next();
|
||||
}
|
||||
if (permissions.hasPermission(req.authInfo.scope,permission)) {
|
||||
return next();
|
||||
}
|
||||
log.audit({event: "permission.fail", permissions: permission},req);
|
||||
return res.status(401).end();
|
||||
});
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ensureClientSecret(req,res,next) {
|
||||
if (!req.body.client_secret) {
|
||||
req.body.client_secret = 'not_available';
|
||||
}
|
||||
next();
|
||||
}
|
||||
function authenticateClient(req,res,next) {
|
||||
return passport.authenticate(['oauth2-client-password'], {session: false})(req,res,next);
|
||||
}
|
||||
function getToken(req,res,next) {
|
||||
return server.token()(req,res,next);
|
||||
}
|
||||
|
||||
function login(req,res) {
|
||||
var response = {};
|
||||
if (settings.adminAuth) {
|
||||
var mergedAdminAuth = Object.assign({}, settings.adminAuth, settings.adminAuth.module);
|
||||
if (mergedAdminAuth.type === "credentials") {
|
||||
response = {
|
||||
"type":"credentials",
|
||||
"prompts":[{id:"username",type:"text",label:"user.username"},{id:"password",type:"password",label:"user.password"}]
|
||||
}
|
||||
} else if (mergedAdminAuth.type === "strategy") {
|
||||
|
||||
var urlPrefix = (settings.httpAdminRoot==='/')?"":settings.httpAdminRoot;
|
||||
response = {
|
||||
"type":"strategy",
|
||||
"prompts":[{type:"button",label:mergedAdminAuth.strategy.label, url: urlPrefix + "auth/strategy"}]
|
||||
}
|
||||
if (mergedAdminAuth.strategy.icon) {
|
||||
response.prompts[0].icon = mergedAdminAuth.strategy.icon;
|
||||
}
|
||||
if (mergedAdminAuth.strategy.image) {
|
||||
response.prompts[0].image = theme.serveFile('/login/',mergedAdminAuth.strategy.image);
|
||||
}
|
||||
}
|
||||
if (theme.context().login && theme.context().login.image) {
|
||||
response.image = theme.context().login.image;
|
||||
}
|
||||
}
|
||||
res.json(response);
|
||||
}
|
||||
|
||||
function revoke(req,res) {
|
||||
var token = req.body.token;
|
||||
// TODO: audit log
|
||||
Tokens.revoke(token).then(function() {
|
||||
log.audit({event: "auth.login.revoke"},req);
|
||||
if (settings.editorTheme && settings.editorTheme.logout && settings.editorTheme.logout.redirect) {
|
||||
res.json({redirect:settings.editorTheme.logout.redirect});
|
||||
} else {
|
||||
res.status(200).end();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function completeVerify(profile,done) {
|
||||
Users.authenticate(profile).then(function(user) {
|
||||
if (user) {
|
||||
Tokens.create(user.username,"node-red-editor",user.permissions).then(function(tokens) {
|
||||
log.audit({event: "auth.login",username:user.username,scope:user.permissions});
|
||||
user.tokens = tokens;
|
||||
done(null,user);
|
||||
});
|
||||
} else {
|
||||
log.audit({event: "auth.login.fail.oauth",username:typeof profile === "string"?profile:profile.username});
|
||||
done(null,false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
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 = {
|
||||
init: init,
|
||||
needsPermission: needsPermission,
|
||||
ensureClientSecret: ensureClientSecret,
|
||||
authenticateClient: authenticateClient,
|
||||
getToken: getToken,
|
||||
errorHandler: function(err,req,res,next) {
|
||||
//TODO: audit log statment
|
||||
//console.log(err.stack);
|
||||
//log.log({level:"audit",type:"auth",msg:err.toString()});
|
||||
return server.errorHandler()(err,req,res,next);
|
||||
},
|
||||
login: login,
|
||||
revoke: revoke,
|
||||
genericStrategy: genericStrategy
|
||||
}
|
Reference in New Issue
Block a user