@5minds/node-red-contrib-processcube 1.5.2-feature-4b06ce-m3644zlp → 1.5.3

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.2-feature-4b06ce-m3644zlp",
3
+ "version": "1.5.3",
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-hotfix-a73235-m346kg1n",
60
+ "@5minds/processcube_engine_client": "^5.1.0",
61
61
  "jwt-decode": "^4.0.0",
62
62
  "openid-client": "^5.5.0"
63
63
  },
@@ -2,6 +2,8 @@ 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
+
5
7
  module.exports = function (RED) {
6
8
  function ProcessCubeEngineNode(n) {
7
9
  RED.nodes.createNode(this, n);
@@ -34,55 +36,6 @@ module.exports = function (RED) {
34
36
  }
35
37
  };
36
38
 
37
- async function getFreshIdentity(url, node) {
38
- try {
39
- if (
40
- !RED.util.evaluateNodeProperty(n.clientId, n.clientIdType, node) ||
41
- !RED.util.evaluateNodeProperty(n.clientSecret, n.clientSecretType, node)
42
- ) {
43
- return null;
44
- }
45
-
46
- const res = await fetch(url + '/atlas_engine/api/v1/authority', {
47
- method: 'GET',
48
- headers: {
49
- Authorization: `Bearer ZHVtbXlfdG9rZW4=`,
50
- 'Content-Type': 'application/json',
51
- },
52
- });
53
-
54
- const body = await res.json();
55
-
56
- const issuer = await oidc.Issuer.discover(body);
57
-
58
- const client = new issuer.Client({
59
- client_id: RED.util.evaluateNodeProperty(n.clientId, n.clientIdType, node),
60
- client_secret: RED.util.evaluateNodeProperty(n.clientSecret, n.clientSecretType, node),
61
- });
62
-
63
- const tokenSet = await client.grant({
64
- grant_type: 'client_credentials',
65
- scope: 'engine_etw engine_read engine_write',
66
- });
67
-
68
- const accessToken = tokenSet.access_token;
69
- const decodedToken = jwt.jwtDecode(accessToken);
70
-
71
- const freshIdentity = {
72
- token: tokenSet.access_token,
73
- userId: decodedToken.sub,
74
- };
75
-
76
- node.setIdentity(freshIdentity);
77
-
78
- return freshIdentity;
79
- } catch (e) {
80
- console.log('Could not get fresh identity', e);
81
- node.error('Could not get fresh identity');
82
- node.error(e);
83
- }
84
- }
85
-
86
39
  node.on('close', async () => {
87
40
  if (this.engineClient) {
88
41
  this.engineClient.dispose();
@@ -91,7 +44,25 @@ module.exports = function (RED) {
91
44
  });
92
45
 
93
46
  if (this.credentials.clientId && this.credentials.clientSecret) {
94
- this.engineClient = new engine_client.EngineClient(this.url, () => getFreshIdentity(this.url, node));
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
+ });
95
66
  } else {
96
67
  this.engineClient = new engine_client.EngineClient(this.url);
97
68
  }
@@ -103,3 +74,85 @@ module.exports = function (RED) {
103
74
  },
104
75
  });
105
76
  };
77
+
78
+ async function getFreshTokenSet(clientId, clientSecret, authorityUrl) {
79
+ const issuer = await oidc.Issuer.discover(authorityUrl);
80
+
81
+ const client = new issuer.Client({
82
+ client_id: clientId,
83
+ client_secret: clientSecret,
84
+ });
85
+
86
+ const tokenSet = await client.grant({
87
+ grant_type: 'client_credentials',
88
+ scope: 'engine_etw engine_read engine_write',
89
+ });
90
+
91
+ return tokenSet;
92
+ }
93
+
94
+ function getIdentityForExternalTaskWorkers(tokenSet) {
95
+ const accessToken = tokenSet.access_token;
96
+ const decodedToken = jwt.jwtDecode(accessToken);
97
+
98
+ return {
99
+ token: tokenSet.access_token,
100
+ userId: decodedToken.sub,
101
+ };
102
+ }
103
+
104
+ async function getExpiresInForExternalTaskWorkers(tokenSet) {
105
+ let expiresIn = tokenSet.expires_in;
106
+
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
+ }
114
+
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;
145
+ }
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
+ }
155
+ };
156
+
157
+ await refresh();
158
+ }
@@ -29,9 +29,8 @@ module.exports = function (RED) {
29
29
 
30
30
  try {
31
31
  const result = await client.processInstances.query({
32
- processModelId: modelId,
33
- identity: engine.identity,
34
- });
32
+ processModelId: modelId
33
+ }, { identity: engine.identity });
35
34
 
36
35
  let allInstances = result.processInstances.filter((instance) => instance.state != 'suspended');
37
36