@ti-engine/core 1.0.10 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/README.md +1 -1
- package/bin/settings.json +2 -0
- package/components/exchange/default/default-message-receiver.js +1 -1
- package/components/exchange/default/default-message-sender.js +1 -1
- package/components/exchange/message-exchange.js +2 -1
- package/components/exchange/message-handler.js +18 -0
- package/components/exchange/message-receiver.js +34 -0
- package/components/exchange/message-sender.js +10 -4
- package/package.json +4 -4
- package/utils/config.js +26 -14
- package/utils/exceptions.js +1 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
##Version 1.0.11
|
|
2
|
+
* feat(message exchange): implement message tampering and insertion protection
|
|
3
|
+
* feat(config): provide option to turn on/off the message tampering and insertion protection
|
|
4
|
+
* feat(config): provide option to turn on/off message tracing
|
|
5
|
+
* feat: upgrade all used packages to their latest versions
|
|
6
|
+
* refactor(config)!: rename all environment variables that set configuration settings to match their related setting
|
|
7
|
+
* docs: add a change log
|
package/README.md
CHANGED
|
@@ -95,7 +95,7 @@ The architectural approach for the **ti-engine** is done in _tiers_ with lower t
|
|
|
95
95
|
|
|
96
96
|
There are three general tiers in the **ti-engine**:
|
|
97
97
|
1. message exchange
|
|
98
|
-
2. service
|
|
98
|
+
2. service domains
|
|
99
99
|
3. solution implementation
|
|
100
100
|
|
|
101
101
|
See the following sections for more information on each of them.
|
package/bin/settings.json
CHANGED
|
@@ -74,7 +74,7 @@ class DefaultMessageReceiver extends MessageReceiver {
|
|
|
74
74
|
onReceive() {
|
|
75
75
|
return new Promise( ( resolve, reject ) => {
|
|
76
76
|
this.#memoryCache.receiveMessage( this.receiveQueue ).then( ( lightweightMessage ) => {
|
|
77
|
-
return this.#memoryCache.retrieveMessagePayload( lightweightMessage, config.getSetting( config.setting.
|
|
77
|
+
return this.#memoryCache.retrieveMessagePayload( lightweightMessage, config.getSetting( config.setting.MESSAGE_EXCHANGE_MESSAGE_STORE ) );
|
|
78
78
|
} ).then( ( message ) => {
|
|
79
79
|
resolve( message );
|
|
80
80
|
} ).catch( ( error ) => {
|
|
@@ -42,7 +42,7 @@ class DefaultMessageSender extends MessageSender {
|
|
|
42
42
|
*/
|
|
43
43
|
onSend( message, queue ) {
|
|
44
44
|
return new Promise( ( resolve, reject ) => {
|
|
45
|
-
this.#memoryCache.storeMessagePayload( message.payload, config.getSetting( config.setting.
|
|
45
|
+
this.#memoryCache.storeMessagePayload( message.payload, config.getSetting( config.setting.MESSAGE_EXCHANGE_MESSAGE_STORE ) ).then( ( storeID ) => {
|
|
46
46
|
let lightweightMessage = _.cloneDeep( message );
|
|
47
47
|
lightweightMessage.payload = storeID;
|
|
48
48
|
return this.#memoryCache.sendMessage( lightweightMessage, queue );
|
|
@@ -25,8 +25,9 @@ const messageTracer = require( "#message-tracer" );
|
|
|
25
25
|
* @property {string} chainID Unique identifier of the message chain if the message is part of one.
|
|
26
26
|
* @property {number} chainLevel The node level of this message in the message chain tree.
|
|
27
27
|
* @property {MessageDestination} destination The destination of the message.
|
|
28
|
+
* @property {string} [hash] Security hash for the message if the mechanism is enabled.
|
|
28
29
|
* @property {string} messageID Unique message identifier.
|
|
29
|
-
* @property {Object|string|undefined} payload The message contents to be processed in destination. If string
|
|
30
|
+
* @property {Object|string|undefined} payload The message contents to be processed in destination. If string it is ID of the payload in the memory cache instead.
|
|
30
31
|
* Note that if this is not an Object or a string, there is no guarantee that it will be delivered in the same/proper format!
|
|
31
32
|
* @property {MessageSource} source The source of the message.
|
|
32
33
|
*/
|
|
@@ -5,8 +5,11 @@
|
|
|
5
5
|
|
|
6
6
|
const ConnectionObserver = require( "#connection-observer" );
|
|
7
7
|
const _ = require( "lodash" );
|
|
8
|
+
const blake2 = require( "blake2" );
|
|
8
9
|
const exceptions = require( "#exceptions" );
|
|
9
10
|
const logger = require( "#logger" );
|
|
11
|
+
const tools = require( "#tools" );
|
|
12
|
+
const config = require( "#config" );
|
|
10
13
|
|
|
11
14
|
/**
|
|
12
15
|
* An abstract class that defines a basic message handler behavior.
|
|
@@ -98,6 +101,21 @@ class MessageHandler extends ConnectionObserver {
|
|
|
98
101
|
return Promise.reject( exceptions.raise( exceptions.exceptionCode.E_GEN_ABSTRACT_METHOD_CALL, { name: this.constructor.name + "." + this.disable.name } ) );
|
|
99
102
|
}
|
|
100
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Used to create a security hash from the message.
|
|
106
|
+
*
|
|
107
|
+
* @method
|
|
108
|
+
* @param {Message} message
|
|
109
|
+
* @returns {string}
|
|
110
|
+
* @public
|
|
111
|
+
*/
|
|
112
|
+
createMessageHash( message ) {
|
|
113
|
+
let transformed = tools.decomposeJSON( tools.decycle( message ) );
|
|
114
|
+
let hash = blake2.createKeyedHash( "blake2b", Buffer.from( config.getSetting( config.setting.MESSAGE_EXCHANGE_SECURITY_HASH_KEY ) ) );
|
|
115
|
+
hash.update( Buffer.from( transformed ) );
|
|
116
|
+
return hash.digest( "hex" );
|
|
117
|
+
}
|
|
118
|
+
|
|
101
119
|
/**
|
|
102
120
|
* Used to register a new {@link MessageObserver} for events related to the messages passing through this handler.
|
|
103
121
|
*
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
const MessageHandler = require( "#message-handler" );
|
|
7
7
|
const logger = require( "#logger" );
|
|
8
8
|
const exceptions = require( "#exceptions" );
|
|
9
|
+
const config = require( "#config" );
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* An abstract class that defines a basic message receiver behavior.
|
|
@@ -84,6 +85,8 @@ class MessageReceiver extends MessageHandler {
|
|
|
84
85
|
*/
|
|
85
86
|
receive() {
|
|
86
87
|
this.onReceive().then( ( message ) => {
|
|
88
|
+
return this.#postReceive( message );
|
|
89
|
+
} ).then( ( message ) => {
|
|
87
90
|
this.onMessage( message );
|
|
88
91
|
} ).catch( ( error ) => {
|
|
89
92
|
logger.log( `Error while trying to receive the next pending message from memory cache in receiver '${ this.connectionIdentifier }'! Resuming operation...`, logger.logSeverity.ERROR, error );
|
|
@@ -108,6 +111,37 @@ class MessageReceiver extends MessageHandler {
|
|
|
108
111
|
return Promise.reject( exceptions.raise( exceptions.exceptionCode.E_GEN_ABSTRACT_METHOD_CALL, { name: this.constructor.name + "." + this.onReceive.name } ) );
|
|
109
112
|
}
|
|
110
113
|
|
|
114
|
+
/* Private interface */
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Used to process the received message before providing it to any {@link MessageObserver}.
|
|
118
|
+
*
|
|
119
|
+
* @method
|
|
120
|
+
* @param {Message} message
|
|
121
|
+
* @returns {Promise<Message>}
|
|
122
|
+
* @private
|
|
123
|
+
*/
|
|
124
|
+
#postReceive( message ) {
|
|
125
|
+
return new Promise( ( resolve, reject ) => {
|
|
126
|
+
if ( config.getSetting( config.setting.MESSAGE_EXCHANGE_SECURITY_HASH_ENABLED ) === true ) {
|
|
127
|
+
let receivedHash = message.hash;
|
|
128
|
+
delete message.hash;
|
|
129
|
+
let currentHash = this.createMessageHash( message );
|
|
130
|
+
if ( receivedHash && receivedHash === currentHash ) {
|
|
131
|
+
resolve( message );
|
|
132
|
+
} else {
|
|
133
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_SEC_MESSAGE_TAMPERING_DETECTED, {
|
|
134
|
+
messageID: message.messageID,
|
|
135
|
+
receivedHash: receivedHash,
|
|
136
|
+
currentHash: currentHash
|
|
137
|
+
} ) );
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
resolve( message );
|
|
141
|
+
}
|
|
142
|
+
} );
|
|
143
|
+
}
|
|
144
|
+
|
|
111
145
|
}
|
|
112
146
|
|
|
113
147
|
module.exports = MessageReceiver;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
const MessageHandler = require( "#message-handler" );
|
|
7
7
|
const exceptions = require( "#exceptions" );
|
|
8
|
+
const config = require( "#config" );
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* An abstract class that defines a basic message sender behavior.
|
|
@@ -70,7 +71,7 @@ class MessageSender extends MessageHandler {
|
|
|
70
71
|
*/
|
|
71
72
|
send( message, queue ) {
|
|
72
73
|
return new Promise( ( resolve, reject ) => {
|
|
73
|
-
this.#preSend().then( () => {
|
|
74
|
+
this.#preSend( message ).then( ( message ) => {
|
|
74
75
|
return this.onSend( message, queue );
|
|
75
76
|
} ).then( () => {
|
|
76
77
|
return this.#postSend();
|
|
@@ -106,12 +107,17 @@ class MessageSender extends MessageHandler {
|
|
|
106
107
|
* Used to do pre-send verifications and checks.
|
|
107
108
|
*
|
|
108
109
|
* @method
|
|
109
|
-
* @
|
|
110
|
+
* @param {Message} message
|
|
111
|
+
* @returns {Promise<Message>}
|
|
110
112
|
* @private
|
|
111
113
|
*/
|
|
112
|
-
#preSend() {
|
|
114
|
+
#preSend( message ) {
|
|
113
115
|
if ( this.isAvailable === true ) {
|
|
114
|
-
|
|
116
|
+
if ( config.getSetting( config.setting.MESSAGE_EXCHANGE_SECURITY_HASH_ENABLED ) === true ) {
|
|
117
|
+
message.hash = this.createMessageHash( message );
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return Promise.resolve( message );
|
|
115
121
|
} else {
|
|
116
122
|
return Promise.reject( exceptions.raise( exceptions.exceptionCode.E_COM_MESSAGE_SENDER_UNAVAILABLE ) );
|
|
117
123
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ti-engine/core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"description": "The ti-engine is an open source, free to use - both for personal and commercial projects - framework for the creation of microservice-based solutions using node.js.",
|
|
5
5
|
"author": "Boris Kostadinov <kostadinov.boris@gmail.com>",
|
|
6
6
|
"license": "ISC",
|
|
@@ -41,12 +41,12 @@
|
|
|
41
41
|
"#tools": "./utils/tools.js"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"blake2": "^4.0
|
|
45
|
-
"dotenv": "^
|
|
44
|
+
"blake2": "^4.1.0",
|
|
45
|
+
"dotenv": "^14.1.0",
|
|
46
46
|
"fs-extra": "^10.0.0",
|
|
47
47
|
"lodash": "^4.17.21",
|
|
48
48
|
"node-schedule": "^2.1.0",
|
|
49
|
-
"ioredis": "^4.28.
|
|
49
|
+
"ioredis": "^4.28.3"
|
|
50
50
|
},
|
|
51
51
|
"optionalDependencies": {
|
|
52
52
|
"@google-cloud/error-reporting": "^2.0.4"
|
package/utils/config.js
CHANGED
|
@@ -20,13 +20,16 @@ const tools = require( "#tools" );
|
|
|
20
20
|
* @property {EnvironmentVariable} env.TI_INSTANCE_CONFIG
|
|
21
21
|
* @property {EnvironmentVariable} env.TI_INSTANCE_ID
|
|
22
22
|
* @property {EnvironmentVariable} env.TI_INSTANCE_NAME
|
|
23
|
-
* @property {EnvironmentVariable} env.
|
|
24
|
-
* @property {EnvironmentVariable} env.
|
|
25
|
-
* @property {EnvironmentVariable} env.
|
|
23
|
+
* @property {EnvironmentVariable} env.TI_AUDITING_LOG_CONSOLE_ENABLED
|
|
24
|
+
* @property {EnvironmentVariable} env.TI_AUDITING_LOG_MIN_LEVEL
|
|
25
|
+
* @property {EnvironmentVariable} env.TI_AUDITING_LOG_USES_JSON
|
|
26
26
|
* @property {EnvironmentVariable} env.TI_MEMORY_CACHE_AUTH_KEY
|
|
27
|
-
* @property {EnvironmentVariable} env.
|
|
28
|
-
* @property {EnvironmentVariable} env.
|
|
29
|
-
* @property {EnvironmentVariable} env.
|
|
27
|
+
* @property {EnvironmentVariable} env.TI_MEMORY_CACHE_REDIS_DB
|
|
28
|
+
* @property {EnvironmentVariable} env.TI_MEMORY_CACHE_REDIS_HOST
|
|
29
|
+
* @property {EnvironmentVariable} env.TI_MEMORY_CACHE_REDIS_PORT
|
|
30
|
+
* @property {EnvironmentVariable} env.TI_MESSAGE_EXCHANGE_SECURITY_HASH_ENABLED
|
|
31
|
+
* @property {EnvironmentVariable} env.TI_MESSAGE_EXCHANGE_SECURITY_HASH_KEY
|
|
32
|
+
* @property {EnvironmentVariable} env.TI_MESSAGE_EXCHANGE_TRACE_LOG_ENABLED
|
|
30
33
|
* @property {EnvironmentVariable} env.TI_OPERATION_MODE
|
|
31
34
|
*/
|
|
32
35
|
|
|
@@ -70,6 +73,8 @@ const tools = require( "#tools" );
|
|
|
70
73
|
* @typedef {Object} SettingsMessageExchange
|
|
71
74
|
* @property {string} messageQueuePrefix
|
|
72
75
|
* @property {string} messageStore
|
|
76
|
+
* @property {boolean} securityHashEnabled
|
|
77
|
+
* @property {string} securityHashKey
|
|
73
78
|
* @property {boolean} traceLogEnabled
|
|
74
79
|
*/
|
|
75
80
|
|
|
@@ -100,7 +105,9 @@ let settingsEnum = tools.enum( {
|
|
|
100
105
|
MEMORY_CACHE_REDIS_HOST: [ "memoryCache.redisHost", "redisHost", "" ],
|
|
101
106
|
MEMORY_CACHE_REDIS_PORT: [ "memoryCache.redisPort", "redisPort", "" ],
|
|
102
107
|
MESSAGE_EXCHANGE_QUEUE_PREFIX: [ "messageExchange.messageQueuePrefix", "messageQueuePrefix", "" ],
|
|
103
|
-
|
|
108
|
+
MESSAGE_EXCHANGE_MESSAGE_STORE: [ "messageExchange.messageStore", "messageStore", "" ],
|
|
109
|
+
MESSAGE_EXCHANGE_SECURITY_HASH_ENABLED: [ "messageExchange.securityHashEnabled", "securityHashEnabled", "" ],
|
|
110
|
+
MESSAGE_EXCHANGE_SECURITY_HASH_KEY: [ "messageExchange.securityHashKey", "securityHashKey", "" ],
|
|
104
111
|
MESSAGE_EXCHANGE_TRACE_LOG_ENABLED: [ "messageExchange.traceLogEnabled", "traceLogEnabled", "" ],
|
|
105
112
|
SERVICE_EXECUTION_TIMEOUT: [ "serviceConfig.executionTimeout", "executionTimeout", "" ],
|
|
106
113
|
SERVICE_HEALTH_CHECK_ADDRESS: [ "serviceConfig.healthCheckAddress", "healthCheckAddress", "" ],
|
|
@@ -120,18 +127,23 @@ const settings = require( "#settings" );
|
|
|
120
127
|
|
|
121
128
|
// override remaining settings with ENV variables (if provided):
|
|
122
129
|
if ( settings.auditing ) {
|
|
123
|
-
settings.auditing.logMinLevel = ( process.env.
|
|
124
|
-
settings.auditing.logConsoleEnabled = ( process.env.
|
|
125
|
-
settings.auditing.logUsesJSON = ( process.env.
|
|
130
|
+
settings.auditing.logMinLevel = ( process.env.TI_AUDITING_LOG_MIN_LEVEL !== undefined ) ? process.env.TI_AUDITING_LOG_MIN_LEVEL : settings.auditing.logMinLevel;
|
|
131
|
+
settings.auditing.logConsoleEnabled = ( process.env.TI_AUDITING_LOG_CONSOLE_ENABLED !== undefined ) ? tools.toBool( process.env.TI_AUDITING_LOG_CONSOLE_ENABLED ) : settings.auditing.logConsoleEnabled;
|
|
132
|
+
settings.auditing.logUsesJSON = ( process.env.TI_AUDITING_LOG_USES_JSON !== undefined ) ? tools.toBool( process.env.TI_AUDITING_LOG_USES_JSON ) : settings.auditing.logUsesJSON;
|
|
126
133
|
}
|
|
127
134
|
if ( settings.memoryCache ) {
|
|
128
135
|
settings.memoryCache.authKey = ( process.env.TI_MEMORY_CACHE_AUTH_KEY !== undefined ) ? process.env.TI_MEMORY_CACHE_AUTH_KEY : settings.memoryCache.authKey;
|
|
129
|
-
settings.memoryCache.redisDB = ( process.env.
|
|
130
|
-
settings.memoryCache.redisHost = ( process.env.
|
|
131
|
-
settings.memoryCache.redisPort = ( process.env.
|
|
136
|
+
settings.memoryCache.redisDB = ( process.env.TI_MEMORY_CACHE_REDIS_DB !== undefined ) ? process.env.TI_MEMORY_CACHE_REDIS_DB : settings.memoryCache.redisDB;
|
|
137
|
+
settings.memoryCache.redisHost = ( process.env.TI_MEMORY_CACHE_REDIS_HOST !== undefined ) ? process.env.TI_MEMORY_CACHE_REDIS_HOST : settings.memoryCache.redisHost;
|
|
138
|
+
settings.memoryCache.redisPort = ( process.env.TI_MEMORY_CACHE_REDIS_PORT !== undefined ) ? process.env.TI_MEMORY_CACHE_REDIS_PORT : settings.memoryCache.redisPort;
|
|
139
|
+
}
|
|
140
|
+
if ( settings.messageExchange ) {
|
|
141
|
+
settings.messageExchange.securityHashEnabled = ( process.env.TI_MESSAGE_EXCHANGE_SECURITY_HASH_ENABLED !== undefined ) ? tools.toBool( process.env.TI_MESSAGE_EXCHANGE_SECURITY_HASH_ENABLED ) : settings.messageExchange.securityHashEnabled;
|
|
142
|
+
settings.messageExchange.securityHashKey = ( process.env.TI_MESSAGE_EXCHANGE_SECURITY_HASH_KEY !== undefined ) ? process.env.TI_MESSAGE_EXCHANGE_SECURITY_HASH_KEY : settings.messageExchange.securityHashKey;
|
|
143
|
+
settings.messageExchange.traceLogEnabled = ( process.env.TI_MESSAGE_EXCHANGE_TRACE_LOG_ENABLED !== undefined ) ? tools.toBool( process.env.TI_MESSAGE_EXCHANGE_TRACE_LOG_ENABLED ) : settings.messageExchange.traceLogEnabled;
|
|
132
144
|
}
|
|
133
145
|
|
|
134
|
-
// make sure GCloud is enabled before trying to
|
|
146
|
+
// make sure GCloud is enabled before trying to set it up:
|
|
135
147
|
if ( process.env.TI_GCLOUD_ENABLED === true && settings.gcloudIntegration ) {
|
|
136
148
|
settings.gcloudIntegration.apiKey = ( process.env.TI_GCLOUD_API_KEY !== undefined ) ? process.env.TI_GCLOUD_API_KEY : settings.gcloudIntegration.apiKey;
|
|
137
149
|
settings.gcloudIntegration.projectID = ( process.env.TI_GCLOUD_PROJECT_ID !== undefined ) ? process.env.TI_GCLOUD_PROJECT_ID : settings.gcloudIntegration.projectID;
|
package/utils/exceptions.js
CHANGED
|
@@ -25,6 +25,7 @@ let exceptionCodeEnum = tools.enum( {
|
|
|
25
25
|
E_SEC_INVALID_AUTH_TOKEN: [ 2000, "invalid auth token", "Invalid authorization token provided." ],
|
|
26
26
|
E_SEC_INVALID_EXPIRED_SESSION: [ 2001, "invalid or expired session", "Invalid or expired session encountered." ],
|
|
27
27
|
E_SEC_UNAUTHORIZED_ACCESS: [ 2002, "unauthorized access", "Attempt for unauthorized access detected." ],
|
|
28
|
+
E_SEC_MESSAGE_TAMPERING_DETECTED: [ 2003, "message tampering detected", "The system detected tampering with the message received via message exchange." ],
|
|
28
29
|
/** Cross-Application Communication exceptions - codes under 3xxx */
|
|
29
30
|
E_COM_GENERAL_ERROR: [ 3000, "general communication error", "General error during cross-application communication." ],
|
|
30
31
|
E_COM_MESSAGE_SENDER_UNAVAILABLE: [ 3001, "message sender unavailable", "The message sender instance is currently unavailable." ],
|