iotagent-node-lib
Version:
IoT Agent library to interface with NGSI Context Broker
598 lines (531 loc) • 19.6 kB
JavaScript
/*
* Copyright 2014 Telefonica Investigación y Desarrollo, S.A.U
*
* This file is part of fiware-iotagent-lib
*
* fiware-iotagent-lib is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* fiware-iotagent-lib is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with fiware-iotagent-lib.
* If not, see http://www.gnu.org/licenses/.
*
* For those usages not covered by the GNU Affero General Public License
* please contact with::daniel.moranjimenez@telefonica.com
*
* Modified by: Federico M. Facca - Martel Innovate
* Modified by: Daniel Calvo - ATOS Research & Innovation
* Modified by: Fernando López - FIWARE Foundation
*/
let config = {};
const logger = require('logops');
let registry;
let groupRegistry;
let commandRegistry;
let securityService;
const fs = require('fs');
const path = require('path');
const SECRETS_DIR = process.env.SECRETS_DIR || '/run/secrets';
const secrets = {};
if (fs.existsSync(SECRETS_DIR)) {
const files = fs.readdirSync(SECRETS_DIR);
files.forEach(function (file) {
const fullPath = path.join(SECRETS_DIR, file);
const key = file;
try {
const data = fs.readFileSync(fullPath, 'utf8').toString().trim();
secrets[key] = data;
} catch (e) {
logger.error(e.message);
}
});
}
function anyIsSet(variableSet) {
for (let i = 0; i < variableSet.length; i++) {
if (process.env[variableSet[i]]) {
return true;
}
}
return false;
}
/**
* If an ENV is a protected Docker Secret extract the value of the secret data
*/
function getSecretData(key) {
const filepath = process.env[key + '_FILE'];
if (filepath) {
process.env[key] = secrets[path.parse(filepath).base] || process.env[key];
}
}
/*
* Inform the user if security is correctly enabled.
*/
function logAuthState() {
const stars = '***********************************************';
if (config.authentication === undefined) {
logger.warn(
stars +
'\n' +
'WARNING: authentication for secure connections is not in use,\n' +
'It is recommended to enable authentication\n' +
stars
);
} else {
const authKeystone = !!config.authentication.user && !!config.authentication.password;
const authKeyrock = !!config.authentication.clientSecret && !!config.authentication.clientId;
if (authKeystone) {
logger.info('INFO: IoT Agent=>Keystone Auth credentials have been configured');
} else if (authKeyrock) {
logger.info('INFO: IoT Agent=>Keyrock Auth credentials have been configured');
} else {
logger.warn(
stars +
'\n' +
'WARNING: authentication for secure connections is in use,\n' +
' but default Auth credentials have not been overridden\n' +
stars
);
}
}
}
/**
* Looks for environment variables that could override configuration values.
*/
function processEnvironmentVariables() {
const environmentVariables = [
'IOTA_CB_URL',
'IOTA_CB_HOST',
'IOTA_CB_PORT',
'IOTA_CB_NGSI_VERSION',
'IOTA_NORTH_HOST',
'IOTA_NORTH_PORT',
'IOTA_PROVIDER_URL',
'IOTA_AUTH_ENABLED',
'IOTA_AUTH_TYPE',
'IOTA_AUTH_HEADER',
'IOTA_AUTH_URL',
'IOTA_AUTH_HOST',
'IOTA_AUTH_PORT',
'IOTA_AUTH_USER',
'IOTA_AUTH_PASSWORD',
'IOTA_AUTH_CLIENT_ID',
'IOTA_AUTH_CLIENT_SECRET',
'IOTA_AUTH_TOKEN_PATH',
'IOTA_AUTH_PERMANENT_TOKEN',
'IOTA_REGISTRY_TYPE',
'IOTA_LOG_LEVEL',
'IOTA_TIMESTAMP',
'IOTA_IOTAM_HOST',
'IOTA_IOTAM_PORT',
'IOTA_IOTAM_PATH',
'IOTA_IOTAM_PROTOCOL',
'IOTA_IOTAM_DESCRIPTION',
'IOTA_DEFAULT_RESOURCE',
'IOTA_EXPLICIT_ATTRS',
'IOTA_MONGO_HOST',
'IOTA_MONGO_PORT',
'IOTA_MONGO_DB',
'IOTA_MONGO_REPLICASET',
'IOTA_MONGO_PASSWORD',
'IOTA_MONGO_AUTH_SOURCE',
'IOTA_MONGO_RETRIES',
'IOTA_MONGO_USER',
'IOTA_MONGO_RETRY_TIME',
'IOTA_POLLING_EXPIRATION',
'IOTA_POLLING_DAEMON_FREQ',
'IOTA_MULTI_CORE',
'IOTA_RELAX_TEMPLATE_VALIDATION',
'IOTA_DEFAULT_ENTITY_NAME_CONJUNCTION',
'IOTA_JSON_LD_CONTEXT',
'IOTA_FALLBACK_TENANT',
'IOTA_FALLBACK_PATH',
'IOTA_LD_SUPPORT_NULL',
'IOTA_LD_SUPPORT_DATASET_ID',
'IOTA_EXPRESS_LIMIT',
'IOTA_USE_CB_FLOW_CONTROL',
'IOTA_STORE_LAST_MEASURE'
];
const iotamVariables = [
'IOTA_IOTAM_URL',
'IOTA_IOTAM_HOST',
'IOTA_IOTAM_PORT',
'IOTA_IOTAM_PATH',
'IOTA_IOTAM_PROTOCOL',
'IOTA_IOTAM_DESCRIPTION',
'IOTA_IOTAM_AGENTPATH'
];
const mongoVariables = [
'IOTA_MONGO_HOST',
'IOTA_MONGO_PORT',
'IOTA_MONGO_DB',
'IOTA_MONGO_REPLICASET',
'IOTA_MONGO_USER',
'IOTA_MONGO_PASSWORD',
'IOTA_MONGO_AUTH_SOURCE',
'IOTA_MONGO_RETRIES',
'IOTA_MONGO_RETRY_TIME',
'IOTA_MONGO_SSL',
'IOTA_MONGO_EXTRAARGS'
];
const protectedVariables = [
'IOTA_AUTH_USER',
'IOTA_AUTH_PASSWORD',
'IOTA_AUTH_CLIENT_ID',
'IOTA_AUTH_CLIENT_SECRET',
'IOTA_MONGO_USER',
'IOTA_MONGO_PASSWORD'
];
// Substitute Docker Secret Variables where set.
protectedVariables.forEach((key) => {
getSecretData(key);
});
environmentVariables.forEach((key) => {
let value = process.env[key];
if (value) {
if (
key.endsWith('USER') ||
key.endsWith('PASSWORD') ||
key.endsWith('CLIENT_ID') ||
key.endsWith('CLIENT_SECRET')
) {
value = '********';
}
logger.info('Setting %s to environment value: %s', key, value);
}
});
// Context Broker Configuration (ensuring the configuration sub-object exists before start using it)
if (config.contextBroker === undefined) {
config.contextBroker = {};
}
if (process.env.IOTA_CB_URL) {
config.contextBroker.url = process.env.IOTA_CB_URL;
// Not sure if config.contextBroker.host and config.contextBroker.port are used
// in this case, but better to have them initialized
if (process.env.IOTA_CB_HOST) {
config.contextBroker.host = process.env.IOTA_CB_HOST;
}
if (process.env.IOTA_CB_PORT) {
config.contextBroker.port = process.env.IOTA_CB_PORT;
}
} else if (process.env.IOTA_CB_HOST) {
config.contextBroker.host = process.env.IOTA_CB_HOST;
config.contextBroker.url = 'http://' + process.env.IOTA_CB_HOST;
if (process.env.IOTA_CB_PORT) {
config.contextBroker.url += ':' + process.env.IOTA_CB_PORT;
} else {
config.contextBroker.url += ':' + config.contextBroker.port;
}
}
if (process.env.IOTA_CB_NGSI_VERSION) {
config.contextBroker.ngsiVersion = process.env.IOTA_CB_NGSI_VERSION;
}
if (process.env.IOTA_JSON_LD_CONTEXT) {
config.contextBroker.jsonLdContext = process.env.IOTA_JSON_LD_CONTEXT.split(',').map((ctx) => ctx.trim());
}
if (Array.isArray(config.contextBroker.jsonLdContext) && config.contextBroker.jsonLdContext.length === 1) {
config.contextBroker.jsonLdContext = config.contextBroker.jsonLdContext[0];
}
config.contextBroker.fallbackTenant =
process.env.IOTA_FALLBACK_TENANT || config.contextBroker.service || 'iotagent';
config.contextBroker.fallbackPath = process.env.IOTA_FALLBACK_PATH || config.contextBroker.subservice || '/';
// North Port Configuration (ensuring the configuration sub-object exists before start using it)
if (config.server === undefined) {
config.server = {};
}
if (process.env.IOTA_NORTH_HOST) {
config.server.host = process.env.IOTA_NORTH_HOST;
}
if (process.env.IOTA_NORTH_PORT) {
config.server.port = process.env.IOTA_NORTH_PORT;
}
config.server.ldSupport = config.server.ldSupport || { null: true, datasetId: true, merge: false };
if (process.env.IOTA_LD_SUPPORT_NULL) {
config.server.ldSupport.null = process.env.IOTA_LD_SUPPORT_NULL === 'true';
}
if (process.env.IOTA_LD_SUPPORT_DATASET_ID) {
config.server.ldSupport.datasetId = process.env.IOTA_LD_SUPPORT_DATASET_ID === 'true';
}
if (process.env.IOTA_LD_SUPPORT_MERGE) {
config.server.ldSupport.datasetId = process.env.IOTA_LD_SUPPORT_MERGE === 'true';
}
if (process.env.IOTA_PROVIDER_URL) {
config.providerUrl = process.env.IOTA_PROVIDER_URL;
}
// Authentication Parameters - General
if (process.env.IOTA_AUTH_ENABLED) {
config.authentication = {};
config.authentication.enabled = process.env.IOTA_AUTH_ENABLED === 'true';
}
if (process.env.IOTA_AUTH_TYPE) {
config.authentication.type = process.env.IOTA_AUTH_TYPE;
}
if (process.env.IOTA_AUTH_HEADER) {
config.authentication.header = process.env.IOTA_AUTH_HEADER;
}
if (process.env.IOTA_AUTH_URL) {
config.authentication.url = process.env.IOTA_AUTH_URL;
} else if (process.env.IOTA_AUTH_HOST) {
config.authentication.host = process.env.IOTA_AUTH_HOST;
config.authentication.url = 'http://' + process.env.IOTA_AUTH_HOST;
if (process.env.IOTA_AUTH_PORT) {
config.authentication.url += ':' + process.env.IOTA_AUTH_PORT;
} else {
config.authentication.url += ':' + config.authentication.port;
}
}
if (process.env.IOTA_AUTH_HOST) {
config.authentication.host = process.env.IOTA_AUTH_HOST;
}
if (process.env.IOTA_AUTH_PORT) {
config.authentication.port = process.env.IOTA_AUTH_PORT;
}
// Authentication Parameters - Oauth + Keyrock
if (process.env.IOTA_AUTH_CLIENT_ID) {
config.authentication.clientId = process.env.IOTA_AUTH_CLIENT_ID;
}
if (process.env.IOTA_AUTH_CLIENT_SECRET) {
config.authentication.clientSecret = process.env.IOTA_AUTH_CLIENT_SECRET;
}
if (process.env.IOTA_AUTH_TOKEN_PATH) {
config.authentication.tokenPath = process.env.IOTA_AUTH_TOKEN_PATH;
}
// Authentication Parameters - Keyrock only
if (process.env.IOTA_AUTH_PERMANENT_TOKEN) {
config.authentication.permanentToken = process.env.IOTA_AUTH_PERMANENT_TOKEN;
}
// Authentication Parameters - Keystone only
if (process.env.IOTA_AUTH_USER) {
config.authentication.user = process.env.IOTA_AUTH_USER;
}
if (process.env.IOTA_AUTH_PASSWORD) {
config.authentication.password = process.env.IOTA_AUTH_PASSWORD;
}
// Registry configuration (memory or database)
config.deviceRegistry = config.deviceRegistry || {};
if (process.env.IOTA_REGISTRY_TYPE) {
config.deviceRegistry.type = process.env.IOTA_REGISTRY_TYPE;
}
// Log Level configuration
if (process.env.IOTA_LOG_LEVEL) {
config.logLevel = process.env.IOTA_LOG_LEVEL;
logger.setLevel(process.env.IOTA_LOG_LEVEL);
}
// Whether to include timestamps
if (process.env.IOTA_TIMESTAMP) {
config.timestamp = process.env.IOTA_TIMESTAMP === 'true';
}
// Default resource
if (process.env.IOTA_DEFAULT_RESOURCE !== undefined) {
config.defaultResource = process.env.IOTA_DEFAULT_RESOURCE;
}
// Default transport
if (process.env.IOTA_DEFAULT_TRANSPORT !== undefined) {
config.defaultTransport = process.env.IOTA_DEFAULT_TRANSPORT;
}
// Default explicitAttrs
if (process.env.IOTA_EXPLICIT_ATTRS !== undefined) {
config.explicitAttrs = process.env.IOTA_EXPLICIT_ATTRS;
}
// IoT Manager Configuration
if (anyIsSet(iotamVariables)) {
config.iotManager = {};
}
if (process.env.IOTA_IOTAM_URL) {
config.iotManager.url = process.env.IOTA_IOTAM_URL;
} else if (process.env.IOTA_IOTAM_HOST) {
config.iotManager.url = 'http://' + process.env.IOTA_IOTAM_HOST;
if (process.env.IOTA_IOTAM_PORT) {
config.iotManager.url += ':' + process.env.IOTA_IOTAM_PORT;
} else {
config.iotManager.url += ':' + config.iotManager.port;
}
}
if (process.env.IOTA_IOTAM_PATH) {
config.iotManager.path = process.env.IOTA_IOTAM_PATH;
}
if (process.env.IOTA_IOTAM_PROTOCOL) {
config.iotManager.protocol = process.env.IOTA_IOTAM_PROTOCOL;
}
if (process.env.IOTA_IOTAM_DESCRIPTION) {
config.iotManager.description = process.env.IOTA_IOTAM_DESCRIPTION;
}
if (process.env.IOTA_IOTAM_AGENTPATH) {
config.iotManager.agentPath = process.env.IOTA_IOTAM_AGENTPATH;
}
// Mongo DB configuration
if (anyIsSet(mongoVariables)) {
config.mongodb = {};
}
if (process.env.IOTA_MONGO_HOST) {
config.mongodb.host = process.env.IOTA_MONGO_HOST;
}
if (process.env.IOTA_MONGO_PORT) {
config.mongodb.port = process.env.IOTA_MONGO_PORT;
}
if (process.env.IOTA_MONGO_DB) {
config.mongodb.db = process.env.IOTA_MONGO_DB;
}
if (process.env.IOTA_MONGO_REPLICASET) {
config.mongodb.replicaSet = process.env.IOTA_MONGO_REPLICASET;
}
if (process.env.IOTA_MONGO_USER) {
config.mongodb.user = process.env.IOTA_MONGO_USER;
}
if (process.env.IOTA_MONGO_PASSWORD) {
config.mongodb.password = process.env.IOTA_MONGO_PASSWORD;
}
if (process.env.IOTA_MONGO_AUTH_SOURCE) {
config.mongodb.authSource = process.env.IOTA_MONGO_AUTH_SOURCE;
}
if (process.env.IOTA_MONGO_RETRIES) {
config.mongodb.retries = process.env.IOTA_MONGO_RETRIES;
}
if (process.env.IOTA_MONGO_RETRY_TIME) {
config.mongodb.retryTime = process.env.IOTA_MONGO_RETRY_TIME;
}
if (process.env.IOTA_MONGO_SSL) {
config.mongodb.ssl = process.env.IOTA_MONGO_SSL.toLowerCase() === 'true';
}
if (process.env.IOTA_MONGO_EXTRAARGS) {
try {
const ea = JSON.parse(process.env.IOTA_MONGO_EXTRAARGS);
if (ea !== null && typeof ea === 'object' && ea.constructor === Object) {
config.mongodb.extraArgs = ea;
}
} catch (e) {
// Do nothing
}
}
if (process.env.IOTA_POLLING_EXPIRATION) {
config.pollingExpiration = process.env.IOTA_POLLING_EXPIRATION;
}
if (process.env.IOTA_POLLING_DAEMON_FREQ) {
config.pollingDaemonFrequency = process.env.IOTA_POLLING_DAEMON_FREQ;
}
if (process.env.IOTA_MULTI_CORE) {
config.multiCore = process.env.IOTA_MULTI_CORE === 'true';
} else {
config.multiCore = config.multiCore === true;
}
if (process.env.IOTA_RELAX_TEMPLATE_VALIDATION) {
config.relaxTemplateValidation = process.env.IOTA_RELAX_TEMPLATE_VALIDATION === 'true';
} else {
config.relaxTemplateValidation = config.relaxTemplateValidation === true;
}
if (process.env.IOTA_DEFAULT_ENTITY_NAME_CONJUNCTION) {
config.defaultEntityNameConjunction = process.env.IOTA_DEFAULT_ENTITY_NAME_CONJUNCTION;
} else {
config.defaultEntityNameConjunction = config.defaultEntityNameConjunction
? config.defaultEntityNameConjunction
: ':';
}
if (process.env.IOTA_EXPRESS_LIMIT) {
config.expressLimit = process.env.IOTA_EXPRESS_LIMIT;
} else {
config.expressLimit = config.expressLimit ? config.expressLimit : '1mb';
}
if (process.env.IOTA_USE_CB_FLOW_CONTROL) {
config.useCBflowControl = process.env.IOTA_USE_CB_FLOW_CONTROL === 'true';
} else {
config.useCBflowControl = config.useCBflowControl === true;
}
if (process.env.IOTA_STORE_LAST_MEASURE) {
config.storeLastMeasure = process.env.IOTA_STORE_LAST_MEASURE === 'true';
} else {
config.storeLastMeasure = config.storeLastMeasure === true;
}
}
function setConfig(newConfig) {
config = newConfig;
if (config.logLevel) {
logger.setLevel(config.logLevel);
}
processEnvironmentVariables();
logAuthState();
}
function getConfig() {
return config;
}
function getConfigForTypeInformation() {
// Just return relevant configuration flags
// avoid to include server, authentication, mongodb, orion and iotamanger info
let conf = {
timestamp: config.timestamp,
defaultResource: config.defaultResource,
explicitAttrs: config.explicitAttrs,
pollingExpiration: config.pollingExpiration,
pollingDaemonFrequency: config.pollingDaemonFrequency,
multiCore: config.multiCore,
relaxTemplateValidation: config.relaxTemplateValidation,
defaultEntityNameConjunction: config.defaultEntityNameConjunction,
defaultType: config.defaultType,
useCBflowControl: config.useCBflowControl,
storeLastMeasure: config.storeLastMeasure
};
return conf;
}
function setRegistry(newRegistry) {
registry = newRegistry;
}
function getRegistry() {
return registry;
}
function setGroupRegistry(newGroupRegistry) {
groupRegistry = newGroupRegistry;
}
function getGroupRegistry() {
return groupRegistry;
}
function setCommandRegistry(newCommandRegistry) {
commandRegistry = newCommandRegistry;
}
function getCommandRegistry() {
return commandRegistry;
}
/**
* Returns the supported NGSI format
*
* @return {string} the supported NGSI format
*/
function ngsiVersion() {
if (config && config.contextBroker && config.contextBroker.ngsiVersion) {
return config.contextBroker.ngsiVersion.toLowerCase();
}
return 'unknown';
}
/**
* It checks if a combination of typeInformation or common Config is LD
*
* @return {boolean} Result of the checking
*/
function checkNgsiLD(typeInformation) {
const format = typeInformation.ngsiVersion || ngsiVersion();
return format.toLowerCase() === 'ld';
}
function setSecurityService(newSecurityService) {
securityService = newSecurityService;
}
function getSecurityService() {
return securityService;
}
exports.setConfig = setConfig;
exports.getConfig = getConfig;
exports.getConfigForTypeInformation = getConfigForTypeInformation;
exports.setRegistry = setRegistry;
exports.getRegistry = getRegistry;
exports.setGroupRegistry = setGroupRegistry;
exports.getGroupRegistry = getGroupRegistry;
exports.setCommandRegistry = setCommandRegistry;
exports.getCommandRegistry = getCommandRegistry;
exports.ngsiVersion = ngsiVersion;
exports.checkNgsiLD = checkNgsiLD;
exports.setSecurityService = setSecurityService;
exports.getSecurityService = getSecurityService;
exports.getSecretData = getSecretData;