node-red/packages/node_modules/@node-red/registry/lib/util.js

208 lines
6.5 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.
**/
const path = require("path");
const semver = require("semver");
const url = require("url");
const {events,i18n,log} = require("@node-red/util");
var runtime;
function copyObjectProperties(src,dst,copyList,blockList) {
if (!src) {
return;
}
if (copyList && !blockList) {
copyList.forEach(function(i) {
if (src.hasOwnProperty(i)) {
var propDescriptor = Object.getOwnPropertyDescriptor(src,i);
Object.defineProperty(dst,i,propDescriptor);
}
});
} else if (!copyList && blockList) {
for (var i in src) {
if (src.hasOwnProperty(i) && blockList.indexOf(i) === -1) {
var propDescriptor = Object.getOwnPropertyDescriptor(src,i);
Object.defineProperty(dst,i,propDescriptor);
}
}
}
}
function requireModule(name) {
var moduleInfo = require("./index").getModuleInfo(name);
if (moduleInfo && moduleInfo.path) {
var relPath = path.relative(__dirname, moduleInfo.path);
return require(relPath);
} else {
// Require it here to avoid the circular dependency
return require("./externalModules").require(name);
}
}
function importModule(name) {
var moduleInfo = require("./index").getModuleInfo(name);
if (moduleInfo && moduleInfo.path) {
const moduleFile = url.pathToFileURL(require.resolve(moduleInfo.path));
return import(moduleFile);
} else {
// Require it here to avoid the circular dependency
return require("./externalModules").import(name);
}
}
function createNodeApi(node) {
var red = {
nodes: {},
log: {},
settings: {},
events: events,
hooks: runtime.hooks,
util: runtime.util,
version: runtime.version,
require: requireModule,
import: importModule,
comms: {
publish: function(topic,data,retain) {
events.emit("comms",{
topic: topic,
data: data,
retain: retain
})
}
},
plugins: {
registerPlugin: function(id,definition) {
return runtime.plugins.registerPlugin(node.id,id,definition);
},
get: function(id) {
return runtime.plugins.getPlugin(id);
},
getByType: function(type) {
return runtime.plugins.getPluginsByType(type);
}
},
library: {
register: function(type) {
return runtime.library.register(node.id,type);
}
},
httpNode: runtime.nodeApp,
httpAdmin: runtime.adminApp,
server: runtime.server
}
copyObjectProperties(runtime.nodes,red.nodes,["createNode","getNode","eachNode","addCredentials","getCredentials","deleteCredentials"]);
red.nodes.registerType = function(type,constructor,opts) {
runtime.nodes.registerType(node.id,type,constructor,opts);
}
red.nodes.registerSubflow = function(subflowDef) {
runtime.nodes.registerSubflow(node.id,subflowDef)
}
copyObjectProperties(log,red.log,null,["init"]);
copyObjectProperties(runtime.settings,red.settings,null,["init","load","reset"]);
if (runtime.adminApi) {
red.auth = runtime.adminApi.auth;
} else {
//TODO: runtime.adminApi is always stubbed if not enabled, so this block
// is unused - but may be needed for the unit tests
red.auth = {
needsPermission: function(v) { return function(req,res,next) {next()} }
};
// TODO: stub out httpAdmin/httpNode/server
}
red["_"] = function() {
var args = Array.prototype.slice.call(arguments, 0);
if (args[0].indexOf(":") === -1) {
args[0] = node.namespace+":"+args[0];
}
return i18n._.apply(null,args);
}
return red;
}
function checkAgainstList(module,version,list) {
for (let i=0;i<list.length;i++) {
let rule = list[i];
if (rule.module.test(module)) {
if (version && rule.version) {
if (semver.satisfies(version,rule.version)) {
return rule;
}
} else {
return rule;
}
}
}
}
function checkModuleAllowed(module,version,allowList,denyList) {
if (!allowList && !denyList) {
// Default to allow
return true;
}
if (allowList.length === 0 && denyList.length === 0) {
return true;
}
var allowedRule = checkAgainstList(module,version,allowList);
var deniedRule = checkAgainstList(module,version,denyList);
// console.log("A",allowedRule)
// console.log("D",deniedRule)
if (allowedRule && !deniedRule) {
return true;
}
if (!allowedRule && deniedRule) {
return false;
}
if (!allowedRule && !deniedRule) {
return true;
}
if (allowedRule.wildcardPos !== deniedRule.wildcardPos) {
return allowedRule.wildcardPos > deniedRule.wildcardPos
} else {
// First wildcard in same position.
// Go with the longer matching rule. This isn't going to be 100%
// right, but we are deep into edge cases at this point.
return allowedRule.module.toString().length > deniedRule.module.toString().length
}
return false;
}
function parseModuleList(list) {
list = list || ["*"];
return list.map(rule => {
let m = /^(.+?)(?:@(.*))?$/.exec(rule);
let wildcardPos = m[1].indexOf("*");
wildcardPos = wildcardPos===-1?Infinity:wildcardPos;
return {
module: new RegExp("^"+m[1].replace(/\*/g,".*")+"$"),
version: m[2],
wildcardPos: wildcardPos
}
})
}
module.exports = {
init: function(_runtime) {
runtime = _runtime;
},
createNodeApi: createNodeApi,
parseModuleList: parseModuleList,
checkModuleAllowed: checkModuleAllowed
}