@5minds/node-red-contrib-processcube 1.5.5 → 1.5.6

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@5minds/node-red-contrib-processcube",
3
- "version": "1.5.5",
3
+ "version": "1.5.6",
4
4
  "license": "MIT",
5
5
  "description": "Node-RED nodes for ProcessCube",
6
6
  "scripts": {
@@ -57,7 +57,7 @@
57
57
  "examples": "examples"
58
58
  },
59
59
  "dependencies": {
60
- "@5minds/processcube_engine_client": "^5.1.0",
60
+ "@5minds/processcube_engine_client": "5.1.0-hotfix-a73235-m346kg1n",
61
61
  "jwt-decode": "^4.0.0",
62
62
  "openid-client": "^5.5.0"
63
63
  },
@@ -2,19 +2,20 @@ const engine_client = require('@5minds/processcube_engine_client');
2
2
  const jwt = require('jwt-decode');
3
3
  const oidc = require('openid-client');
4
4
 
5
- const DELAY_FACTOR = 0.85;
6
-
7
5
  module.exports = function (RED) {
8
6
  function ProcessCubeEngineNode(n) {
9
7
  RED.nodes.createNode(this, n);
10
8
  const node = this;
11
9
  const identityChangedCallbacks = [];
12
- this.url = RED.util.evaluateNodeProperty(n.url, n.urlType, node);
13
10
  this.identity = null;
14
11
 
15
12
  this.credentials.clientId = RED.util.evaluateNodeProperty(n.clientId, n.clientIdType, node);
16
13
  this.credentials.clientSecret = RED.util.evaluateNodeProperty(n.clientSecret, n.clientSecretType, node);
17
14
 
15
+ // known issue: kann bei falschem timing zu laufzeitfehlern führen (absprache MM)
16
+ // set the engine url
17
+ const stopRefreshing = periodicallyRefreshEngineClient(this, n, 10000);
18
+
18
19
  this.registerOnIdentityChanged = function (callback) {
19
20
  identityChangedCallbacks.push(callback);
20
21
  };
@@ -36,123 +37,95 @@ module.exports = function (RED) {
36
37
  }
37
38
  };
38
39
 
39
- node.on('close', async () => {
40
- if (this.engineClient) {
41
- this.engineClient.dispose();
42
- this.engineClient = null;
40
+ function periodicallyRefreshEngineClient(node, n, intervalMs) {
41
+ function refreshUrl() {
42
+ const newUrl = RED.util.evaluateNodeProperty(n.url, n.urlType, node);
43
+
44
+ if (node.url == newUrl) {
45
+ return;
46
+ }
47
+
48
+ node.url = newUrl;
49
+ if (node.credentials.clientId && node.credentials.clientSecret) {
50
+ if (this.engineClient) {
51
+ this.engineClient.dispose();
52
+ }
53
+ node.engineClient = new engine_client.EngineClient(node.url, () =>
54
+ getFreshIdentity(node.url, node)
55
+ );
56
+ } else {
57
+ if (this.engineClient) {
58
+ this.engineClient.dispose();
59
+ }
60
+ node.engineClient = new engine_client.EngineClient(node.url);
61
+ }
43
62
  }
44
- });
45
63
 
46
- if (this.credentials.clientId && this.credentials.clientSecret) {
47
- this.engineClient = new engine_client.EngineClient(this.url);
48
-
49
- this.engineClient.applicationInfo
50
- .getAuthorityAddress()
51
- .then((authorityUrl) => {
52
- startRefreshingIdentityCycle(
53
- this.credentials.clientId,
54
- this.credentials.clientSecret,
55
- authorityUrl,
56
- node
57
- ).catch((reason) => {
58
- console.error(reason);
59
- node.error(reason);
60
- });
61
- })
62
- .catch((reason) => {
63
- console.error(reason);
64
- node.error(reason);
65
- });
66
- } else {
67
- this.engineClient = new engine_client.EngineClient(this.url);
64
+ refreshUrl();
65
+ const intervalId = setInterval(refreshUrl, intervalMs);
66
+
67
+ return () => clearInterval(intervalId);
68
68
  }
69
- }
70
- RED.nodes.registerType('processcube-engine-config', ProcessCubeEngineNode, {
71
- credentials: {
72
- clientId: { type: 'text' },
73
- clientSecret: { type: 'password' },
74
- },
75
- });
76
- };
77
69
 
78
- async function getFreshTokenSet(clientId, clientSecret, authorityUrl) {
79
- const issuer = await oidc.Issuer.discover(authorityUrl);
70
+ async function getFreshIdentity(url, node) {
71
+ try {
72
+ if (
73
+ !RED.util.evaluateNodeProperty(n.clientId, n.clientIdType, node) ||
74
+ !RED.util.evaluateNodeProperty(n.clientSecret, n.clientSecretType, node)
75
+ ) {
76
+ return null;
77
+ }
78
+
79
+ const res = await fetch(url + '/atlas_engine/api/v1/authority', {
80
+ method: 'GET',
81
+ headers: {
82
+ Authorization: `Bearer ZHVtbXlfdG9rZW4=`,
83
+ 'Content-Type': 'application/json',
84
+ },
85
+ });
80
86
 
81
- const client = new issuer.Client({
82
- client_id: clientId,
83
- client_secret: clientSecret,
84
- });
87
+ const body = await res.json();
85
88
 
86
- const tokenSet = await client.grant({
87
- grant_type: 'client_credentials',
88
- scope: 'engine_etw engine_read engine_write',
89
- });
89
+ const issuer = await oidc.Issuer.discover(body);
90
90
 
91
- return tokenSet;
92
- }
91
+ const client = new issuer.Client({
92
+ client_id: RED.util.evaluateNodeProperty(n.clientId, n.clientIdType, node),
93
+ client_secret: RED.util.evaluateNodeProperty(n.clientSecret, n.clientSecretType, node),
94
+ });
93
95
 
94
- function getIdentityForExternalTaskWorkers(tokenSet) {
95
- const accessToken = tokenSet.access_token;
96
- const decodedToken = jwt.jwtDecode(accessToken);
96
+ const tokenSet = await client.grant({
97
+ grant_type: 'client_credentials',
98
+ scope: 'engine_etw engine_read engine_write',
99
+ });
97
100
 
98
- return {
99
- token: tokenSet.access_token,
100
- userId: decodedToken.sub,
101
- };
102
- }
101
+ const accessToken = tokenSet.access_token;
102
+ const decodedToken = jwt.jwtDecode(accessToken);
103
103
 
104
- async function getExpiresInForExternalTaskWorkers(tokenSet) {
105
- let expiresIn = tokenSet.expires_in;
104
+ const freshIdentity = {
105
+ token: tokenSet.access_token,
106
+ userId: decodedToken.sub,
107
+ };
106
108
 
107
- if (!expiresIn && tokenSet.expires_at) {
108
- expiresIn = Math.floor(tokenSet.expires_at - Date.now() / 1000);
109
- }
110
-
111
- if (expiresIn === undefined) {
112
- throw new Error('Could not determine the time until the access token for external task workers expires');
113
- }
109
+ node.setIdentity(freshIdentity);
114
110
 
115
- return expiresIn;
116
- }
117
-
118
- /**
119
- * Start refreshing the identity in regular intervals.
120
- * @param {TokenSet | null} tokenSet The token set to refresh the identity for
121
- * @returns {Promise<void>} A promise that resolves when the timer for refreshing the identity is initialized
122
- * */
123
- async function startRefreshingIdentityCycle(clientId, clientSecret, authorityUrl, configNode) {
124
- let retries = 5;
125
-
126
- const refresh = async () => {
127
- try {
128
- const newTokenSet = await getFreshTokenSet(clientId, clientSecret, authorityUrl);
129
- const expiresIn = await getExpiresInForExternalTaskWorkers(newTokenSet);
130
- const delay = expiresIn * DELAY_FACTOR * 1000;
131
-
132
- freshIdentity = getIdentityForExternalTaskWorkers(newTokenSet);
133
-
134
- configNode.setIdentity(freshIdentity);
135
-
136
- retries = 5;
137
- setTimeout(refresh, delay);
138
- } catch (error) {
139
- if (retries === 0) {
140
- console.error(
141
- 'Could not refresh identity for external task worker processes. Stopping all external task workers.',
142
- { error }
143
- );
144
- return;
111
+ return freshIdentity;
112
+ } catch (e) {
113
+ node.error(`Could not get fresh identity: ${e}`);
145
114
  }
146
- console.error('Could not refresh identity for external task worker processes.', {
147
- error,
148
- retryCount: retries,
149
- });
150
- retries--;
151
-
152
- const delay = 2 * 1000;
153
- setTimeout(refresh, delay);
154
115
  }
155
- };
156
116
 
157
- await refresh();
158
- }
117
+ node.on('close', async () => {
118
+ if (this.engineClient) {
119
+ stopRefreshing();
120
+ this.engineClient.dispose();
121
+ this.engineClient = null;
122
+ }
123
+ });
124
+ }
125
+ RED.nodes.registerType('processcube-engine-config', ProcessCubeEngineNode, {
126
+ credentials: {
127
+ clientId: { type: 'text' },
128
+ clientSecret: { type: 'password' },
129
+ },
130
+ });
131
+ };