mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
1df2f5e96a
Fixes #2642
180 lines
5.8 KiB
JavaScript
180 lines
5.8 KiB
JavaScript
/**
|
|
* 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 BearerStrategy = require('passport-http-bearer').Strategy;
|
|
var ClientPasswordStrategy = require('passport-oauth2-client-password').Strategy;
|
|
|
|
var passport = require("passport");
|
|
var crypto = require("crypto");
|
|
var util = require("util");
|
|
|
|
var Tokens = require("./tokens");
|
|
var Users = require("./users");
|
|
var Clients = require("./clients");
|
|
var permissions = require("./permissions");
|
|
|
|
var log = require("@node-red/util").log; // TODO: separate module
|
|
|
|
var bearerStrategy = function (accessToken, done) {
|
|
// is this a valid token?
|
|
Tokens.get(accessToken).then(function(token) {
|
|
if (token) {
|
|
Users.get(token.user).then(function(user) {
|
|
if (user) {
|
|
done(null,user,{scope:token.scope});
|
|
} else {
|
|
log.audit({event: "auth.invalid-token"});
|
|
done(null,false);
|
|
}
|
|
});
|
|
} else {
|
|
log.audit({event: "auth.invalid-token"});
|
|
done(null,false);
|
|
}
|
|
});
|
|
}
|
|
bearerStrategy.BearerStrategy = new BearerStrategy(bearerStrategy);
|
|
|
|
var clientPasswordStrategy = function(clientId, clientSecret, done) {
|
|
Clients.get(clientId).then(function(client) {
|
|
if (client && client.secret == clientSecret) {
|
|
done(null,client);
|
|
} else {
|
|
log.audit({event: "auth.invalid-client",client:clientId});
|
|
done(null,false);
|
|
}
|
|
});
|
|
}
|
|
clientPasswordStrategy.ClientPasswordStrategy = new ClientPasswordStrategy(clientPasswordStrategy);
|
|
|
|
var loginAttempts = [];
|
|
var loginSignInWindow = 600000; // 10 minutes
|
|
|
|
|
|
var passwordTokenExchange = function(client, username, password, scope, done) {
|
|
var now = Date.now();
|
|
loginAttempts = loginAttempts.filter(function(logEntry) {
|
|
return logEntry.time + loginSignInWindow > now;
|
|
});
|
|
loginAttempts.push({time:now, user:username});
|
|
var attemptCount = 0;
|
|
loginAttempts.forEach(function(logEntry) {
|
|
/* istanbul ignore else */
|
|
if (logEntry.user == username) {
|
|
attemptCount++;
|
|
}
|
|
});
|
|
if (attemptCount > 5) {
|
|
log.audit({event: "auth.login.fail.too-many-attempts",username:username,client:client.id});
|
|
done(new Error("Too many login attempts. Wait 10 minutes and try again"),false);
|
|
return;
|
|
}
|
|
|
|
Users.authenticate(username,password).then(function(user) {
|
|
if (user) {
|
|
if (scope === "") {
|
|
scope = user.permissions;
|
|
}
|
|
if (permissions.hasPermission(user.permissions,scope)) {
|
|
loginAttempts = loginAttempts.filter(function(logEntry) {
|
|
return logEntry.user !== username;
|
|
});
|
|
Tokens.create(username,client.id,scope).then(function(tokens) {
|
|
log.audit({event: "auth.login",username:username,client:client.id,scope:scope});
|
|
done(null,tokens.accessToken,null,{expires_in:tokens.expires_in});
|
|
});
|
|
} else {
|
|
log.audit({event: "auth.login.fail.permissions",username:username,client:client.id,scope:scope});
|
|
done(null,false);
|
|
}
|
|
} else {
|
|
log.audit({event: "auth.login.fail.credentials",username:username,client:client.id,scope:scope});
|
|
done(null,false);
|
|
}
|
|
});
|
|
}
|
|
|
|
function AnonymousStrategy() {
|
|
passport.Strategy.call(this);
|
|
this.name = 'anon';
|
|
}
|
|
util.inherits(AnonymousStrategy, passport.Strategy);
|
|
AnonymousStrategy.prototype.authenticate = function(req) {
|
|
var self = this;
|
|
Users.default().then(function(anon) {
|
|
if (anon) {
|
|
self.success(anon,{scope:anon.permissions});
|
|
} else {
|
|
self.fail(401);
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
function authenticateUserToken(req) {
|
|
return new Promise( (resolve,reject) => {
|
|
var token = null;
|
|
var tokenHeader = Users.tokenHeader();
|
|
if (Users.tokenHeader() === null) {
|
|
// No custom user token provided. Fail the request
|
|
reject();
|
|
return;
|
|
} else if (Users.tokenHeader() === 'authorization') {
|
|
if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
|
|
token = req.headers.authorization.split(' ')[1];
|
|
}
|
|
} else {
|
|
token = req.headers[Users.tokenHeader()];
|
|
}
|
|
if (token) {
|
|
Users.tokens(token).then(function(user) {
|
|
if (user) {
|
|
resolve(user);
|
|
} else {
|
|
reject();
|
|
}
|
|
});
|
|
} else {
|
|
reject();
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
function TokensStrategy() {
|
|
passport.Strategy.call(this);
|
|
this.name = 'tokens';
|
|
}
|
|
util.inherits(TokensStrategy, passport.Strategy);
|
|
TokensStrategy.prototype.authenticate = function(req) {
|
|
authenticateUserToken(req).then(user => {
|
|
this.success(user,{scope:user.permissions});
|
|
}).catch(err => {
|
|
this.fail(401);
|
|
});
|
|
}
|
|
|
|
|
|
|
|
module.exports = {
|
|
bearerStrategy: bearerStrategy,
|
|
clientPasswordStrategy: clientPasswordStrategy,
|
|
passwordTokenExchange: passwordTokenExchange,
|
|
anonymousStrategy: new AnonymousStrategy(),
|
|
tokensStrategy: new TokensStrategy(),
|
|
authenticateUserToken: authenticateUserToken
|
|
}
|