soajs.controller 4.3.2 → 4.3.4

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/lib/parseURL.js CHANGED
@@ -8,7 +8,20 @@
8
8
  * found in the LICENSE file at the root of this repository
9
9
  */
10
10
 
11
- const url = require('url');
11
+ /**
12
+ * Parse URL using WHATWG URL API and return object compatible with legacy url.parse()
13
+ * @param {string} urlString - URL string to parse
14
+ * @returns {Object} - Object with pathname, path, and query properties
15
+ */
16
+ const parseURL = (urlString) => {
17
+ const urlObj = new URL(urlString, 'http://localhost');
18
+ const search = urlObj.search || '';
19
+ return {
20
+ pathname: urlObj.pathname,
21
+ path: urlObj.pathname + search,
22
+ query: Object.fromEntries(urlObj.searchParams)
23
+ };
24
+ };
12
25
 
13
26
 
14
27
  module.exports = (uri, parsedUrl) => {
@@ -16,7 +29,7 @@ module.exports = (uri, parsedUrl) => {
16
29
  let service_nv = serviceInfo[1];
17
30
  let service_n = service_nv;
18
31
  let service_v = null;
19
-
32
+
20
33
  //check if there is /v1 or v1.1 in the url
21
34
  let matches = uri.match(/\/v[0-9]+(.[0-9]+)?\//);
22
35
  if (matches && matches.length > 0) {
@@ -25,7 +38,7 @@ module.exports = (uri, parsedUrl) => {
25
38
  service_v = hit.replace("v", '');
26
39
  serviceInfo.splice(2, 1);
27
40
  uri = uri.replace(matches[0], "/");
28
- parsedUrl = url.parse(uri, true);
41
+ parsedUrl = parseURL(uri);
29
42
  }
30
43
  }
31
44
 
@@ -52,11 +52,12 @@ function getRouteRegExp(route) {
52
52
  * Check if the current request should trigger lastSeen
53
53
  * @param {Object} include - The include whitelist configuration
54
54
  * @param {string} serviceName - The current service name
55
- * @param {string} apiPath - The current API path
55
+ * @param {string} apiPath - The current API path (relative to service, e.g., "/active")
56
+ * @param {string} fullPath - The full API path (e.g., "/connectspaces/active")
56
57
  * @param {string} method - The HTTP method (GET, POST, etc.)
57
58
  * @returns {boolean} - true if lastSeen should be triggered
58
59
  */
59
- function shouldTriggerLastSeen(include, serviceName, apiPath, method) {
60
+ function shouldTriggerLastSeen(include, serviceName, apiPath, fullPath, method) {
60
61
  // No include filter = trigger for all (backward compatible)
61
62
  if (!include) {
62
63
  return true;
@@ -74,9 +75,17 @@ function shouldTriggerLastSeen(include, serviceName, apiPath, method) {
74
75
  return true;
75
76
  }
76
77
 
77
- // API-level match
78
+ // API-level match - check both relative path and full path for backward compatibility
78
79
  if (typeof serviceConfig === 'object' && serviceConfig.apis) {
79
- return isApiMethodMatch(serviceConfig.apis, apiPath, method);
80
+ // Try relative path first (e.g., "/active")
81
+ if (apiPath && isApiMethodMatch(serviceConfig.apis, apiPath, method)) {
82
+ return true;
83
+ }
84
+ // Fallback to full path for backward compatibility (e.g., "/connectspaces/active")
85
+ if (fullPath && fullPath !== apiPath && isApiMethodMatch(serviceConfig.apis, fullPath, method)) {
86
+ return true;
87
+ }
88
+ return false;
80
89
  }
81
90
 
82
91
  return false;
@@ -150,11 +159,13 @@ module.exports = () => {
150
159
 
151
160
  // Check include filter
152
161
  let currentService = get(["soajs", "controller", "serviceParams", "name"], req);
153
- let currentApi = get(["soajs", "controller", "serviceParams", "parsedUrl", "pathname"], req);
162
+ // Get both relative path (e.g., "/active") and full path (e.g., "/connectspaces/active")
163
+ let currentApi = get(["soajs", "controller", "serviceParams", "path"], req);
164
+ let fullPath = get(["soajs", "controller", "serviceParams", "parsedUrl", "pathname"], req);
154
165
  let currentMethod = req.method;
155
166
 
156
- if (!shouldTriggerLastSeen(lastSeen.include, currentService, currentApi, currentMethod)) {
157
- req.soajs.log.debug("lastSeen skipped for [" + currentService + " " + currentMethod + " " + currentApi + "]");
167
+ if (!shouldTriggerLastSeen(lastSeen.include, currentService, currentApi, fullPath, currentMethod)) {
168
+ req.soajs.log.debug("lastSeen skipped for [" + currentService + " " + currentMethod + " " + (currentApi || fullPath) + "]");
158
169
  return;
159
170
  }
160
171
 
package/mw/mt/lib.js CHANGED
@@ -13,7 +13,7 @@ let _ = require("lodash");
13
13
 
14
14
  module.exports = {
15
15
  "mergeACLArray": function (arrayACL, cb) {
16
-
16
+
17
17
  //if only one return it
18
18
  if (arrayACL.length === 1) {
19
19
  return cb(null, arrayACL[0]);
@@ -30,30 +30,30 @@ module.exports = {
30
30
  function () {
31
31
  return cb(null, acl);
32
32
  });
33
-
33
+
34
34
  function mergePackageAcl(oneAcl, cb) {
35
35
  //first object
36
36
  if (!acl.acl) {
37
- acl.acl = oneAcl.acl;
37
+ acl.acl = _.cloneDeep(oneAcl.acl);
38
38
  return cb();
39
39
  }
40
40
  async.eachOfSeries(oneAcl.acl, function (service, serviceName, serviceCall) {
41
41
  if (!acl.acl[serviceName]) {
42
- acl.acl[serviceName] = service;
42
+ acl.acl[serviceName] = _.cloneDeep(service);
43
43
  return serviceCall();
44
44
  }
45
45
  async.eachOfSeries(service, function (version, versionValue, versionCall) {
46
46
  if (!acl.acl[serviceName][versionValue]) {
47
- acl.acl[serviceName][versionValue] = version;
47
+ acl.acl[serviceName][versionValue] = _.cloneDeep(version);
48
48
  return serviceCall();
49
49
  }
50
50
  if (version.apisPermission === "restricted") {
51
51
  if (acl.acl[serviceName] && acl.acl[serviceName][versionValue] && acl.acl[serviceName][versionValue].apisPermission && acl.acl[serviceName][versionValue].apisPermission === "restricted") {
52
52
  //only restricted allowed to merge
53
- acl.acl[serviceName][versionValue] = _.merge(acl.acl[serviceName][versionValue], version);
53
+ acl.acl[serviceName][versionValue] = _.merge(acl.acl[serviceName][versionValue], _.cloneDeep(version));
54
54
  return versionCall();
55
55
  } else {
56
- acl.acl[serviceName][versionValue] = version;
56
+ acl.acl[serviceName][versionValue] = _.cloneDeep(version);
57
57
  return versionCall();
58
58
  }
59
59
  } else {
@@ -62,42 +62,42 @@ module.exports = {
62
62
  return versionCall();
63
63
  } else {
64
64
  //only non restricted allowed to merge
65
- acl.acl[serviceName][versionValue] = _.merge(acl.acl[serviceName][versionValue], version);
65
+ acl.acl[serviceName][versionValue] = _.merge(acl.acl[serviceName][versionValue], _.cloneDeep(version));
66
66
  return versionCall();
67
67
  }
68
68
  }
69
69
  }, serviceCall);
70
70
  }, cb);
71
71
  }
72
-
72
+
73
73
  function mergePackageAclAllEnv(oneAcl, cb) {
74
74
  //first object
75
75
  if (!acl.acl_all_env) {
76
- acl.acl_all_env = oneAcl.acl_all_env;
76
+ acl.acl_all_env = _.cloneDeep(oneAcl.acl_all_env);
77
77
  return cb();
78
78
  }
79
79
  async.eachOf(oneAcl.acl_all_env, function (environment, envName, envCall) {
80
80
  if (!acl.acl_all_env[envName]) {
81
- acl.acl_all_env[envName] = environment;
81
+ acl.acl_all_env[envName] = _.cloneDeep(environment);
82
82
  return envCall();
83
83
  }
84
84
  async.eachOf(environment, function (service, serviceName, serviceCall) {
85
85
  if (!acl.acl_all_env[envName][serviceName]) {
86
- acl.acl_all_env[envName][serviceName] = service;
86
+ acl.acl_all_env[envName][serviceName] = _.cloneDeep(service);
87
87
  return serviceCall();
88
88
  }
89
89
  async.eachOf(service, function (version, versionValue, versionCall) {
90
90
  if (!acl.acl_all_env[envName][serviceName][versionValue]) {
91
- acl.acl_all_env[envName][serviceName][versionValue] = version;
91
+ acl.acl_all_env[envName][serviceName][versionValue] = _.cloneDeep(version);
92
92
  return serviceCall();
93
93
  }
94
94
  if (version.apisPermission === "restricted") {
95
95
  if (acl.acl_all_env[envName][serviceName][versionValue].apisPermission && acl.acl_all_env[envName][serviceName][versionValue].apisPermission === "restricted") {
96
96
  //only restricted allowed to merge
97
- acl.acl_all_env[envName][serviceName][versionValue] = _.merge(acl.acl_all_env[envName][serviceName][versionValue], version);
97
+ acl.acl_all_env[envName][serviceName][versionValue] = _.merge(acl.acl_all_env[envName][serviceName][versionValue], _.cloneDeep(version));
98
98
  return versionCall();
99
99
  } else {
100
- acl.acl_all_env[envName][serviceName][versionValue] = version;
100
+ acl.acl_all_env[envName][serviceName][versionValue] = _.cloneDeep(version);
101
101
  return versionCall();
102
102
  }
103
103
  } else {
@@ -106,7 +106,7 @@ module.exports = {
106
106
  return versionCall();
107
107
  } else {
108
108
  //only non restricted allowed to merge
109
- acl.acl_all_env[envName][serviceName][versionValue] = _.merge(acl.acl_all_env[envName][serviceName][versionValue], version);
109
+ acl.acl_all_env[envName][serviceName][versionValue] = _.merge(acl.acl_all_env[envName][serviceName][versionValue], _.cloneDeep(version));
110
110
  return versionCall();
111
111
  }
112
112
  }
@@ -115,4 +115,4 @@ module.exports = {
115
115
  }, cb);
116
116
  }
117
117
  }
118
- };
118
+ };
@@ -31,7 +31,7 @@ module.exports = (configuration) => {
31
31
  }
32
32
  if (!ACL && req.soajs.controller.serviceParams.packObj.acl) {
33
33
  req.soajs.log.debug("soajsRoute keyACL: Found Default ACL at Package level, setting default ACL configuration.");
34
- ACL = req.soajs.controller.serviceParams.packObj.acl;
34
+ ACL = coreLibs.utils.cloneObj(req.soajs.controller.serviceParams.packObj.acl);
35
35
  }
36
36
  let finalACL = null;
37
37
  if (ACL) {
package/mw/url/index.js CHANGED
@@ -8,9 +8,23 @@
8
8
  * found in the LICENSE file at the root of this repository
9
9
  */
10
10
 
11
- const url = require('url');
12
11
  let libParseURL = require('../../lib/parseURL');
13
12
 
13
+ /**
14
+ * Parse URL using WHATWG URL API and return object compatible with legacy url.parse()
15
+ * @param {string} urlString - URL string to parse
16
+ * @returns {Object} - Object with pathname, path, and query properties
17
+ */
18
+ const parseURL = (urlString) => {
19
+ const urlObj = new URL(urlString, 'http://localhost');
20
+ const search = urlObj.search || '';
21
+ return {
22
+ pathname: urlObj.pathname,
23
+ path: urlObj.pathname + search,
24
+ query: Object.fromEntries(urlObj.searchParams)
25
+ };
26
+ };
27
+
14
28
  /**
15
29
  * Assure req param (soajs & soajs.controller)
16
30
  * Parse url and fetch service info (soajs.controller.serviceParams)
@@ -30,7 +44,7 @@ module.exports = (configuration) => {
30
44
  req.soajs.controller = {};
31
45
  }
32
46
 
33
- let parsedUrl = url.parse(req.url, true);
47
+ let parsedUrl = parseURL(req.url);
34
48
  if (!req.query && parsedUrl.query) {
35
49
  req.query = parsedUrl.query;
36
50
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "soajs.controller",
3
3
  "description": "soajs multi tenant API gateway",
4
- "version": "4.3.2",
4
+ "version": "4.3.4",
5
5
  "author": {
6
6
  "name": "soajs team",
7
7
  "email": "team@soajs.org"
@@ -27,9 +27,9 @@
27
27
  "merge": "2.1.1",
28
28
  "netmask": "2.0.2",
29
29
  "oauth2-server": "2.4.1",
30
- "path-to-regexp": "8.3.0",
30
+ "path-to-regexp": "8.4.0",
31
31
  "soajs.core.libs": "1.2.2",
32
- "soajs.core.modules": "5.2.18",
32
+ "soajs.core.modules": "5.2.19",
33
33
  "soajs.urac.driver": "3.2.15",
34
34
  "useragent": "2.3.0"
35
35
  },
@@ -14,8 +14,22 @@ const registryModule = require("./../modules/registry");
14
14
 
15
15
  const httpProxy = require('http-proxy');
16
16
  const http = require('http');
17
- const url = require('url');
18
17
  const soajsLib = require("soajs.core.libs");
18
+
19
+ /**
20
+ * Parse URL using WHATWG URL API and return object compatible with legacy url.parse()
21
+ * @param {string} urlString - URL string to parse
22
+ * @returns {Object} - Object with pathname, path, and query properties
23
+ */
24
+ const parseURL = (urlString) => {
25
+ const urlObj = new URL(urlString, 'http://localhost');
26
+ const search = urlObj.search || '';
27
+ return {
28
+ pathname: urlObj.pathname,
29
+ path: urlObj.pathname + search,
30
+ query: Object.fromEntries(urlObj.searchParams)
31
+ };
32
+ };
19
33
  const soajsUtils = soajsLib.utils;
20
34
 
21
35
  const packageInfo = require("../package.json");
@@ -148,7 +162,7 @@ let Maintenance = (core, log, param, serviceIp, regEnvironment, awareness_mw, so
148
162
  log.error('Internal proxy error: ' + error);
149
163
 
150
164
  res.writeHead(200, {'Content-Type': 'application/json'});
151
- let parsedUrl = url.parse(req.url, true);
165
+ let parsedUrl = parseURL(req.url);
152
166
  let response = maintenanceResponse(parsedUrl, param, '/proxySocket');
153
167
  return res.end(JSON.stringify(response));
154
168
  });
@@ -159,7 +173,7 @@ let Maintenance = (core, log, param, serviceIp, regEnvironment, awareness_mw, so
159
173
  res.writeHead(200, {'Content-Type': 'image/x-icon'});
160
174
  return res.end();
161
175
  }
162
- let parsedUrl = url.parse(req.url, true);
176
+ let parsedUrl = parseURL(req.url);
163
177
 
164
178
  if (parsedUrl.pathname === '/reloadRegistry') {
165
179
  reloadRegistry(parsedUrl, core, log, param, serviceIp, (response) => {
@@ -0,0 +1,11 @@
1
+ - i have 2 users with access to the same tenant
2
+ - under this tenant i have 2 groups as follow:
3
+ - owner with the following packages [GRPCH_DEFAU, GRPCH_OWNER]
4
+ - member with the following packages [GRPCH_DEFAU, GRPCH_AMMBR, GRPCH_EGDTL, GRPCH_SMDIA, GRPCH_SMSSG, GRPCH_VMMBR]
5
+ - i have microservice connectonboarding with an api DELETE /group/chats/users/:userId and this api is only under package GRPCH_OWNER
6
+
7
+ i have the following usecase:
8
+ - load the provision by hitting the maintenance route /loadProvision
9
+ - user1 with member group login first and hit the acl api /soajs/acl, then we check the response and we see that he has the right packages and api DELETE /group/chats/users/:userId is not part of finalACL which is correct
10
+ - user2 with owner group login and hit the acl api /soajs/acl, then we check the response and we see that he has the right packages and api DELETE /group/chats/users/:userId is part of finalACL which is correct
11
+ - user1 with member group hit the acl api /soajs/acl again, then we check the response and we see that he has the right packages but api DELETE /group/chats/users/:userId is now part of finalACL which is not correct
@@ -1,13 +1,27 @@
1
1
  "use strict";
2
2
 
3
- const url = require('url');
4
3
  const helper = require("../../helper.js");
5
4
  const lib = helper.requireModule('./lib/parseURL');
6
5
  const assert = require('assert');
7
6
 
7
+ /**
8
+ * Parse URL using WHATWG URL API and return object compatible with legacy url.parse()
9
+ * @param {string} urlString - URL string to parse
10
+ * @returns {Object} - Object with pathname, path, and query properties
11
+ */
12
+ const parseURL = (urlString) => {
13
+ const urlObj = new URL(urlString, 'http://localhost');
14
+ const search = urlObj.search || '';
15
+ return {
16
+ pathname: urlObj.pathname,
17
+ path: urlObj.pathname + search,
18
+ query: Object.fromEntries(urlObj.searchParams)
19
+ };
20
+ };
21
+
8
22
  describe("Unit test for: lib - parseURL", () => {
9
23
  let uri = '/micro2/v1.1/hello';
10
- let parsedURL = url.parse(uri, true);
24
+ let parsedURL = parseURL(uri);
11
25
  it("Vanilla test with v1.1", (done) => {
12
26
  let response = lib(uri, parsedURL);
13
27
  assert.deepStrictEqual(response.name, 'micro2', "lib.parseURL vanilla test with v1.1 failed for name");
@@ -16,10 +30,10 @@ describe("Unit test for: lib - parseURL", () => {
16
30
  });
17
31
  it("Vanilla test with :1.1", (done) => {
18
32
  uri = '/micro2:1.1/hello';
19
- parsedURL = url.parse(uri, true);
33
+ parsedURL = parseURL(uri);
20
34
  let response = lib(uri, parsedURL);
21
35
  assert.deepStrictEqual(response.name, 'micro2', "lib.parseURL vanilla test with 1:1 failed for name");
22
36
  assert.deepStrictEqual(response.service_v, '1.1', "lib.parseURL vanilla test with 1:1 failed version");
23
37
  done();
24
38
  });
25
- });
39
+ });