@attraccess/plugin-rabbitmq 0.1.0-nightly.938.1 → 0.1.0-nightly.940.1
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/dist/index.js +182 -123
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -42,9 +42,139 @@ var import_common2 = require("@nestjs/common");
|
|
|
42
42
|
|
|
43
43
|
// backend/rabbitmq-detection.service.ts
|
|
44
44
|
var import_common = require("@nestjs/common");
|
|
45
|
+
var import_node_crypto2 = require("node:crypto");
|
|
46
|
+
|
|
47
|
+
// backend/rabbitmq-management-transport.ts
|
|
48
|
+
var import_node_crypto = require("node:crypto");
|
|
49
|
+
var import_node_https = require("node:https");
|
|
50
|
+
var import_node_net = require("node:net");
|
|
51
|
+
var TrustConfigurationError = class extends Error {
|
|
52
|
+
};
|
|
53
|
+
function managementApiBase(config) {
|
|
54
|
+
const host = (0, import_node_net.isIP)(config.host) === 6 ? `[${config.host}]` : config.host;
|
|
55
|
+
return `${config.useTls ? "https" : "http"}://${host}:${config.useTls ? 15671 : 15672}`;
|
|
56
|
+
}
|
|
57
|
+
function trustOptions(config) {
|
|
58
|
+
if (config.tlsInsecure) {
|
|
59
|
+
throw new TrustConfigurationError(
|
|
60
|
+
"RabbitMQ management requires certificate verification. Disable tlsInsecure and configure a trusted CA certificate."
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
const servername = config.tlsServername;
|
|
64
|
+
if (servername != null && (typeof servername !== "string" || servername.length > 253 || !servername.split(".").every((label) => /^[a-z\d](?:[a-z\d-]{0,61}[a-z\d])?$/i.test(label)) || (0, import_node_net.isIP)(servername))) {
|
|
65
|
+
throw new TrustConfigurationError(
|
|
66
|
+
"TLS server name must be a DNS hostname matching the management certificate, without a scheme or port."
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
if (config.caCert != null) {
|
|
70
|
+
try {
|
|
71
|
+
const pem = config.caCert;
|
|
72
|
+
const certificates = typeof pem === "string" ? pem.match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g) : null;
|
|
73
|
+
if (!certificates?.length || pem.replace(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g, "").trim()) {
|
|
74
|
+
throw new Error();
|
|
75
|
+
}
|
|
76
|
+
for (const certificate of certificates) new import_node_crypto.X509Certificate(certificate);
|
|
77
|
+
} catch {
|
|
78
|
+
throw new TrustConfigurationError(
|
|
79
|
+
"Invalid CA certificate. Configure a PEM certificate or PEM certificate bundle."
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
rejectUnauthorized: true,
|
|
85
|
+
...config.caCert != null ? { ca: config.caCert } : {},
|
|
86
|
+
...servername != null ? { servername } : {}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
async function managementRequest(config, url, method, body, timeoutMs) {
|
|
90
|
+
const headers = { accept: "application/json" };
|
|
91
|
+
if (config.username !== null) {
|
|
92
|
+
headers.authorization = `Basic ${Buffer.from(`${config.username}:${config.password ?? ""}`, "utf8").toString("base64")}`;
|
|
93
|
+
}
|
|
94
|
+
const payload = body === void 0 ? void 0 : JSON.stringify(body);
|
|
95
|
+
if (payload !== void 0) headers["content-type"] = "application/json";
|
|
96
|
+
if (!config.useTls) {
|
|
97
|
+
const controller = new AbortController();
|
|
98
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
99
|
+
try {
|
|
100
|
+
return await fetch(url, { method, headers, body: payload, signal: controller.signal });
|
|
101
|
+
} finally {
|
|
102
|
+
clearTimeout(timeout);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const options = trustOptions(config);
|
|
106
|
+
if (new URL(url).protocol !== "https:") {
|
|
107
|
+
throw new TrustConfigurationError("TLS is enabled: the RabbitMQ management endpoint must use HTTPS.");
|
|
108
|
+
}
|
|
109
|
+
return new Promise((resolve, reject) => {
|
|
110
|
+
const req = (0, import_node_https.request)(url, { ...options, agent: false, method, headers }, (res) => {
|
|
111
|
+
const chunks = [];
|
|
112
|
+
let bytes = 0;
|
|
113
|
+
const maximumBytes = 8 * 1024 * 1024;
|
|
114
|
+
res.on("data", (chunk) => {
|
|
115
|
+
bytes += chunk.length;
|
|
116
|
+
if (bytes > maximumBytes) {
|
|
117
|
+
chunks.length = 0;
|
|
118
|
+
req.destroy(Object.assign(new Error(), { code: "RESPONSE_TOO_LARGE" }));
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
chunks.push(Buffer.from(chunk));
|
|
122
|
+
});
|
|
123
|
+
res.on("error", reject);
|
|
124
|
+
res.on("aborted", () => reject(new Error("Response aborted")));
|
|
125
|
+
res.on("end", () => {
|
|
126
|
+
if (bytes > maximumBytes) return;
|
|
127
|
+
const status = res.statusCode ?? 502;
|
|
128
|
+
try {
|
|
129
|
+
if (status < 200 || status > 599) throw new Error();
|
|
130
|
+
resolve(
|
|
131
|
+
new Response([204, 205, 304].includes(status) ? null : Buffer.concat(chunks).toString("utf8"), { status })
|
|
132
|
+
);
|
|
133
|
+
} catch {
|
|
134
|
+
reject(Object.assign(new Error(), { code: "INVALID_RESPONSE_STATUS" }));
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
const timeout = setTimeout(() => req.destroy(Object.assign(new Error(), { code: "ETIMEDOUT" })), timeoutMs);
|
|
139
|
+
req.on("error", reject);
|
|
140
|
+
req.on("close", () => clearTimeout(timeout));
|
|
141
|
+
req.end(payload);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
function describeManagementError(error, timeoutMs) {
|
|
145
|
+
if (error instanceof TrustConfigurationError) return error.message;
|
|
146
|
+
const outer = error;
|
|
147
|
+
const code = outer?.cause?.code ?? outer?.code;
|
|
148
|
+
if (outer?.name === "AbortError" || code === "ETIMEDOUT")
|
|
149
|
+
return `Management API did not respond within ${timeoutMs}ms.`;
|
|
150
|
+
switch (code) {
|
|
151
|
+
case "RESPONSE_TOO_LARGE":
|
|
152
|
+
return "Management API response exceeded the 8 MiB limit.";
|
|
153
|
+
case "INVALID_RESPONSE_STATUS":
|
|
154
|
+
return "Management API returned an invalid HTTP status.";
|
|
155
|
+
case "CERT_HAS_EXPIRED":
|
|
156
|
+
case "CERT_NOT_YET_VALID":
|
|
157
|
+
return "TLS certificate is expired or not yet valid. Check the Attraccess host clock and the certificate validity dates; renew the certificate if necessary.";
|
|
158
|
+
case "ERR_TLS_CERT_ALTNAME_INVALID":
|
|
159
|
+
return "TLS certificate hostname mismatch. Configure tlsServername to match the management certificate DNS name or correct the certificate.";
|
|
160
|
+
case "DEPTH_ZERO_SELF_SIGNED_CERT":
|
|
161
|
+
case "SELF_SIGNED_CERT_IN_CHAIN":
|
|
162
|
+
case "UNABLE_TO_VERIFY_LEAF_SIGNATURE":
|
|
163
|
+
case "UNABLE_TO_GET_ISSUER_CERT":
|
|
164
|
+
case "UNABLE_TO_GET_ISSUER_CERT_LOCALLY":
|
|
165
|
+
return "TLS certificate is not trusted. Configure the issuing CA PEM bundle and ensure the management server sends its intermediate certificates.";
|
|
166
|
+
case "ECONNREFUSED":
|
|
167
|
+
return "Management connection refused. Check the management listener and port.";
|
|
168
|
+
case "ENOTFOUND":
|
|
169
|
+
case "EAI_AGAIN":
|
|
170
|
+
return "Management hostname could not be resolved. Check DNS and the configured host.";
|
|
171
|
+
default:
|
|
172
|
+
return "Failed to reach the RabbitMQ management API. Check connectivity and, for HTTPS, the CA, certificate validity, host clock, and TLS server name.";
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// backend/rabbitmq-detection.service.ts
|
|
45
177
|
var PLUGIN_CONTEXT = /* @__PURE__ */ Symbol.for("attraccess.plugin.context");
|
|
46
|
-
var MGMT_PORT_HTTP = 15672;
|
|
47
|
-
var MGMT_PORT_HTTPS = 15671;
|
|
48
178
|
var CACHE_TTL_MS = 6e4;
|
|
49
179
|
var PROBE_TIMEOUT_MS = 5e3;
|
|
50
180
|
var RabbitmqDetectionService = class {
|
|
@@ -56,18 +186,19 @@ var RabbitmqDetectionService = class {
|
|
|
56
186
|
// Returns a cached verdict when fresh, otherwise probes. `forceRefresh`
|
|
57
187
|
// bypasses the cache (used by the UI's manual refresh).
|
|
58
188
|
async detect(mqttServerId, forceRefresh = false) {
|
|
189
|
+
const config = await this.context.getMqttServerConfig(mqttServerId);
|
|
190
|
+
const configKey = (0, import_node_crypto2.createHash)("sha256").update(JSON.stringify(config ?? null)).digest("hex");
|
|
59
191
|
if (!forceRefresh) {
|
|
60
192
|
const cached = this.cache.get(mqttServerId);
|
|
61
|
-
if (cached && cached.expiresAt > this.now()) {
|
|
193
|
+
if (cached && cached.configKey === configKey && cached.expiresAt > this.now()) {
|
|
62
194
|
return cached.result;
|
|
63
195
|
}
|
|
64
196
|
}
|
|
65
|
-
const result = await this.probe(mqttServerId);
|
|
66
|
-
this.cache.set(mqttServerId, { result, expiresAt: this.now() + CACHE_TTL_MS });
|
|
197
|
+
const result = await this.probe(mqttServerId, config);
|
|
198
|
+
this.cache.set(mqttServerId, { result, configKey, expiresAt: this.now() + CACHE_TTL_MS });
|
|
67
199
|
return result;
|
|
68
200
|
}
|
|
69
|
-
async probe(mqttServerId) {
|
|
70
|
-
const config = await this.context.getMqttServerConfig(mqttServerId);
|
|
201
|
+
async probe(mqttServerId, config) {
|
|
71
202
|
const checkedAt = this.nowIso();
|
|
72
203
|
if (!config) {
|
|
73
204
|
return {
|
|
@@ -143,32 +274,10 @@ var RabbitmqDetectionService = class {
|
|
|
143
274
|
// management API runs on its own port, so we substitute the conventional
|
|
144
275
|
// management port for the MQTT one and pick the scheme from useTls.
|
|
145
276
|
managementApiBase(config) {
|
|
146
|
-
|
|
147
|
-
const port = config.useTls ? MGMT_PORT_HTTPS : MGMT_PORT_HTTP;
|
|
148
|
-
return `${scheme}://${config.host}:${port}`;
|
|
277
|
+
return managementApiBase(config);
|
|
149
278
|
}
|
|
150
279
|
async fetchOverview(managementApi, config) {
|
|
151
|
-
|
|
152
|
-
const timeout = setTimeout(() => controller.abort(), PROBE_TIMEOUT_MS);
|
|
153
|
-
try {
|
|
154
|
-
return await fetch(`${managementApi}/api/overview`, {
|
|
155
|
-
headers: {
|
|
156
|
-
accept: "application/json",
|
|
157
|
-
...this.authHeader(config)
|
|
158
|
-
},
|
|
159
|
-
signal: controller.signal
|
|
160
|
-
});
|
|
161
|
-
} finally {
|
|
162
|
-
clearTimeout(timeout);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
authHeader(config) {
|
|
166
|
-
if (config.username === null) {
|
|
167
|
-
return {};
|
|
168
|
-
}
|
|
169
|
-
const credentials = `${config.username}:${config.password ?? ""}`;
|
|
170
|
-
const encoded = Buffer.from(credentials, "utf8").toString("base64");
|
|
171
|
-
return { authorization: `Basic ${encoded}` };
|
|
280
|
+
return managementRequest(config, `${managementApi}/api/overview`, "GET", void 0, PROBE_TIMEOUT_MS);
|
|
172
281
|
}
|
|
173
282
|
async parseOverview(response) {
|
|
174
283
|
try {
|
|
@@ -178,13 +287,7 @@ var RabbitmqDetectionService = class {
|
|
|
178
287
|
}
|
|
179
288
|
}
|
|
180
289
|
describeError(error) {
|
|
181
|
-
|
|
182
|
-
if (error.name === "AbortError") {
|
|
183
|
-
return `Management API did not respond within ${PROBE_TIMEOUT_MS}ms.`;
|
|
184
|
-
}
|
|
185
|
-
return error.message;
|
|
186
|
-
}
|
|
187
|
-
return "Failed to reach the RabbitMQ management API.";
|
|
290
|
+
return describeManagementError(error, PROBE_TIMEOUT_MS);
|
|
188
291
|
}
|
|
189
292
|
now() {
|
|
190
293
|
return Date.now();
|
|
@@ -222,63 +325,35 @@ RabbitmqDetectionController = __decorateClass([
|
|
|
222
325
|
|
|
223
326
|
// backend/rabbitmq-management-client.ts
|
|
224
327
|
var import_common3 = require("@nestjs/common");
|
|
225
|
-
var MGMT_PORT_HTTP2 = 15672;
|
|
226
|
-
var MGMT_PORT_HTTPS2 = 15671;
|
|
227
328
|
var REQUEST_TIMEOUT_MS = 1e4;
|
|
228
329
|
var RabbitmqManagementClient = class {
|
|
229
330
|
// Builds the management API base URL from the generic MQTT config (same
|
|
230
331
|
// convention as RabbitmqDetectionService).
|
|
231
332
|
managementApiBase(config) {
|
|
232
|
-
|
|
233
|
-
const port = config.useTls ? MGMT_PORT_HTTPS2 : MGMT_PORT_HTTP2;
|
|
234
|
-
return `${scheme}://${config.host}:${port}`;
|
|
333
|
+
return managementApiBase(config);
|
|
235
334
|
}
|
|
236
335
|
// Performs one management API request. `path` is relative to `/api`, with
|
|
237
336
|
// every variable segment already URL-encoded by the caller. Returns the
|
|
238
337
|
// parsed JSON body (or null for empty responses, e.g. 201/204).
|
|
239
338
|
async request(config, method, path, body) {
|
|
240
339
|
const base = this.managementApiBase(config);
|
|
241
|
-
const controller = new AbortController();
|
|
242
|
-
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
243
340
|
let response;
|
|
244
341
|
try {
|
|
245
|
-
response = await
|
|
246
|
-
method,
|
|
247
|
-
headers: {
|
|
248
|
-
accept: "application/json",
|
|
249
|
-
...body !== void 0 ? { "content-type": "application/json" } : {},
|
|
250
|
-
...this.authHeader(config)
|
|
251
|
-
},
|
|
252
|
-
body: body !== void 0 ? JSON.stringify(body) : void 0,
|
|
253
|
-
signal: controller.signal
|
|
254
|
-
});
|
|
342
|
+
response = await managementRequest(config, `${base}/api${path}`, method, body, REQUEST_TIMEOUT_MS);
|
|
255
343
|
} catch (error) {
|
|
256
|
-
throw new import_common3.HttpException(
|
|
257
|
-
`Cannot reach the RabbitMQ management API at ${base}: ${this.describeNetworkError(error)}`,
|
|
258
|
-
import_common3.HttpStatus.BAD_GATEWAY
|
|
259
|
-
);
|
|
260
|
-
} finally {
|
|
261
|
-
clearTimeout(timeout);
|
|
344
|
+
throw new import_common3.HttpException(describeManagementError(error, REQUEST_TIMEOUT_MS), import_common3.HttpStatus.BAD_GATEWAY);
|
|
262
345
|
}
|
|
263
346
|
if (!response.ok) {
|
|
264
347
|
throw await this.toHttpException(response);
|
|
265
348
|
}
|
|
266
349
|
return this.parseBody(response);
|
|
267
350
|
}
|
|
268
|
-
authHeader(config) {
|
|
269
|
-
if (config.username === null) {
|
|
270
|
-
return {};
|
|
271
|
-
}
|
|
272
|
-
const credentials = `${config.username}:${config.password ?? ""}`;
|
|
273
|
-
const encoded = Buffer.from(credentials, "utf8").toString("base64");
|
|
274
|
-
return { authorization: `Basic ${encoded}` };
|
|
275
|
-
}
|
|
276
351
|
// Maps an upstream error response to an exception with an actionable
|
|
277
352
|
// message. Auth failures surface as 502 (not 401/403) so the host frontend
|
|
278
353
|
// doesn't mistake them for an expired Attraccess session.
|
|
279
354
|
async toHttpException(response) {
|
|
280
355
|
const upstream = await this.parseErrorBody(response);
|
|
281
|
-
const reason = upstream?.reason
|
|
356
|
+
const reason = typeof upstream?.reason === "string" ? upstream.reason : typeof upstream?.error === "string" ? upstream.error : "";
|
|
282
357
|
if (response.status === 401) {
|
|
283
358
|
return new import_common3.HttpException(
|
|
284
359
|
"The RabbitMQ management API rejected the configured MQTT server credentials (401). Check the username/password configured for this MQTT server.",
|
|
@@ -287,24 +362,21 @@ var RabbitmqManagementClient = class {
|
|
|
287
362
|
}
|
|
288
363
|
if (response.status === 403 || /not.?authori[sz]ed/i.test(reason)) {
|
|
289
364
|
return new import_common3.HttpException(
|
|
290
|
-
|
|
365
|
+
'The configured MQTT server user lacks management privileges on RabbitMQ. User management requires a user with the "administrator" tag.',
|
|
291
366
|
import_common3.HttpStatus.BAD_GATEWAY
|
|
292
367
|
);
|
|
293
368
|
}
|
|
294
369
|
if (response.status === 404) {
|
|
295
|
-
return new import_common3.HttpException(
|
|
296
|
-
`Not found on the RabbitMQ side${reason ? `: ${reason}` : "."}`,
|
|
297
|
-
import_common3.HttpStatus.NOT_FOUND
|
|
298
|
-
);
|
|
370
|
+
return new import_common3.HttpException("Not found on the RabbitMQ side.", import_common3.HttpStatus.NOT_FOUND);
|
|
299
371
|
}
|
|
300
372
|
if (response.status === 400) {
|
|
301
373
|
return new import_common3.HttpException(
|
|
302
|
-
|
|
374
|
+
"RabbitMQ rejected the request. Check the supplied management parameters.",
|
|
303
375
|
import_common3.HttpStatus.BAD_REQUEST
|
|
304
376
|
);
|
|
305
377
|
}
|
|
306
378
|
return new import_common3.HttpException(
|
|
307
|
-
`RabbitMQ management API request failed (HTTP ${response.status})
|
|
379
|
+
`RabbitMQ management API request failed (HTTP ${response.status}).`,
|
|
308
380
|
import_common3.HttpStatus.BAD_GATEWAY
|
|
309
381
|
);
|
|
310
382
|
}
|
|
@@ -329,19 +401,6 @@ var RabbitmqManagementClient = class {
|
|
|
329
401
|
return null;
|
|
330
402
|
}
|
|
331
403
|
}
|
|
332
|
-
describeNetworkError(error) {
|
|
333
|
-
if (error instanceof Error) {
|
|
334
|
-
if (error.name === "AbortError") {
|
|
335
|
-
return `no response within ${REQUEST_TIMEOUT_MS}ms`;
|
|
336
|
-
}
|
|
337
|
-
const cause = error.cause;
|
|
338
|
-
if (cause instanceof Error && cause.message) {
|
|
339
|
-
return cause.message;
|
|
340
|
-
}
|
|
341
|
-
return error.message;
|
|
342
|
-
}
|
|
343
|
-
return "unknown network error";
|
|
344
|
-
}
|
|
345
404
|
};
|
|
346
405
|
RabbitmqManagementClient = __decorateClass([
|
|
347
406
|
(0, import_common3.Injectable)()
|
|
@@ -781,28 +840,28 @@ var _RabbitmqCredentialProvisioningProvider = class _RabbitmqCredentialProvision
|
|
|
781
840
|
async supports(config) {
|
|
782
841
|
return (await this.detection.detect(config.id)).isRabbitMQ;
|
|
783
842
|
}
|
|
784
|
-
provision(
|
|
785
|
-
return this.writeCredential(
|
|
843
|
+
provision(request2) {
|
|
844
|
+
return this.writeCredential(request2);
|
|
786
845
|
}
|
|
787
|
-
rotate(
|
|
788
|
-
return this.writeCredential(
|
|
846
|
+
rotate(request2) {
|
|
847
|
+
return this.writeCredential(request2);
|
|
789
848
|
}
|
|
790
|
-
async revoke(
|
|
791
|
-
this.assertName(
|
|
792
|
-
const config = await this.requireConfig(
|
|
793
|
-
this.assertNotManagementUser(config,
|
|
849
|
+
async revoke(request2) {
|
|
850
|
+
this.assertName(request2.username, "Username");
|
|
851
|
+
const config = await this.requireConfig(request2.mqttServerId);
|
|
852
|
+
this.assertNotManagementUser(config, request2.username);
|
|
794
853
|
try {
|
|
795
|
-
await this.client.request(config, "DELETE", `/users/${encodeURIComponent(
|
|
854
|
+
await this.client.request(config, "DELETE", `/users/${encodeURIComponent(request2.username)}`);
|
|
796
855
|
} catch (error) {
|
|
797
856
|
if (error instanceof import_common6.HttpException && error.getStatus() === import_common6.HttpStatus.NOT_FOUND) return;
|
|
798
857
|
throw error;
|
|
799
858
|
}
|
|
800
859
|
}
|
|
801
|
-
async writeCredential(
|
|
802
|
-
this.assertRequest(
|
|
803
|
-
const config = await this.requireConfig(
|
|
804
|
-
this.assertNotManagementUser(config,
|
|
805
|
-
const lockKey = `${config.id}:${
|
|
860
|
+
async writeCredential(request2) {
|
|
861
|
+
this.assertRequest(request2);
|
|
862
|
+
const config = await this.requireConfig(request2.mqttServerId);
|
|
863
|
+
this.assertNotManagementUser(config, request2.username);
|
|
864
|
+
const lockKey = `${config.id}:${request2.vhost}`;
|
|
806
865
|
let lock = _RabbitmqCredentialProvisioningProvider.vhostLocks.get(lockKey);
|
|
807
866
|
if (!lock) {
|
|
808
867
|
lock = { mutex: new Mutex(), users: 0 };
|
|
@@ -810,7 +869,7 @@ var _RabbitmqCredentialProvisioningProvider = class _RabbitmqCredentialProvision
|
|
|
810
869
|
}
|
|
811
870
|
lock.users += 1;
|
|
812
871
|
try {
|
|
813
|
-
return await lock.mutex.runExclusive(() => this.writeCredentialLocked(
|
|
872
|
+
return await lock.mutex.runExclusive(() => this.writeCredentialLocked(request2, config));
|
|
814
873
|
} finally {
|
|
815
874
|
lock.users -= 1;
|
|
816
875
|
if (lock.users === 0 && _RabbitmqCredentialProvisioningProvider.vhostLocks.get(lockKey) === lock) {
|
|
@@ -818,10 +877,10 @@ var _RabbitmqCredentialProvisioningProvider = class _RabbitmqCredentialProvision
|
|
|
818
877
|
}
|
|
819
878
|
}
|
|
820
879
|
}
|
|
821
|
-
async writeCredentialLocked(
|
|
880
|
+
async writeCredentialLocked(request2, config) {
|
|
822
881
|
const password = (0, import_crypto.randomBytes)(24).toString("base64url");
|
|
823
|
-
const vhost = encodeURIComponent(
|
|
824
|
-
const username = encodeURIComponent(
|
|
882
|
+
const vhost = encodeURIComponent(request2.vhost);
|
|
883
|
+
const username = encodeURIComponent(request2.username);
|
|
825
884
|
const existing = await this.userExists(config, username);
|
|
826
885
|
const vhostExisted = await this.exists(config, `/vhosts/${vhost}`);
|
|
827
886
|
await this.client.request(config, "PUT", `/vhosts/${vhost}`);
|
|
@@ -833,11 +892,11 @@ var _RabbitmqCredentialProvisioningProvider = class _RabbitmqCredentialProvision
|
|
|
833
892
|
previousPermissions = await this.getPermissions(config, vhost, username);
|
|
834
893
|
previousTopicPermissions = await this.getTopicPermissions(config, vhost, username);
|
|
835
894
|
permissionsCaptured = true;
|
|
836
|
-
await this.writePermissions(config,
|
|
895
|
+
await this.writePermissions(config, request2, vhost, username);
|
|
837
896
|
await this.client.request(config, "PUT", `/users/${username}`, { password, tags: "" });
|
|
838
897
|
} else {
|
|
839
898
|
await this.client.request(config, "PUT", `/users/${username}`, { password, tags: "" });
|
|
840
|
-
await this.writePermissions(config,
|
|
899
|
+
await this.writePermissions(config, request2, vhost, username);
|
|
841
900
|
}
|
|
842
901
|
} catch (error) {
|
|
843
902
|
const rollback = [
|
|
@@ -848,14 +907,14 @@ var _RabbitmqCredentialProvisioningProvider = class _RabbitmqCredentialProvision
|
|
|
848
907
|
}
|
|
849
908
|
return {
|
|
850
909
|
providerId: this.id,
|
|
851
|
-
identity:
|
|
852
|
-
username:
|
|
853
|
-
vhost:
|
|
910
|
+
identity: request2.identity,
|
|
911
|
+
username: request2.username,
|
|
912
|
+
vhost: request2.vhost,
|
|
854
913
|
password
|
|
855
914
|
};
|
|
856
915
|
}
|
|
857
|
-
async writePermissions(config,
|
|
858
|
-
const subscriptionQueue = `mqtt-subscription-${escapeRegex(
|
|
916
|
+
async writePermissions(config, request2, vhost, username) {
|
|
917
|
+
const subscriptionQueue = `mqtt-subscription-${escapeRegex(request2.identity)}.*`;
|
|
859
918
|
await this.client.request(config, "PUT", `/permissions/${vhost}/${username}`, {
|
|
860
919
|
configure: `^${subscriptionQueue}$`,
|
|
861
920
|
write: `^(amq\\.topic|${subscriptionQueue})$`,
|
|
@@ -863,8 +922,8 @@ var _RabbitmqCredentialProvisioningProvider = class _RabbitmqCredentialProvision
|
|
|
863
922
|
});
|
|
864
923
|
await this.client.request(config, "PUT", `/topic-permissions/${vhost}/${username}`, {
|
|
865
924
|
exchange: "amq.topic",
|
|
866
|
-
write: mqttFiltersToRegex(
|
|
867
|
-
read: mqttFiltersToRegex(
|
|
925
|
+
write: mqttFiltersToRegex(request2.topicPolicy.publish),
|
|
926
|
+
read: mqttFiltersToRegex(request2.topicPolicy.subscribe)
|
|
868
927
|
});
|
|
869
928
|
}
|
|
870
929
|
async userExists(config, username) {
|
|
@@ -925,14 +984,14 @@ var _RabbitmqCredentialProvisioningProvider = class _RabbitmqCredentialProvision
|
|
|
925
984
|
}
|
|
926
985
|
return config;
|
|
927
986
|
}
|
|
928
|
-
assertRequest(
|
|
929
|
-
this.assertName(
|
|
930
|
-
this.assertName(
|
|
931
|
-
this.assertName(
|
|
932
|
-
if (!Array.isArray(
|
|
987
|
+
assertRequest(request2) {
|
|
988
|
+
this.assertName(request2.identity, "Identity");
|
|
989
|
+
this.assertName(request2.username, "Username");
|
|
990
|
+
this.assertName(request2.vhost, "Vhost");
|
|
991
|
+
if (!Array.isArray(request2.topicPolicy.publish) || !Array.isArray(request2.topicPolicy.subscribe)) {
|
|
933
992
|
throw new import_common6.BadRequestException("Publish and subscribe topic policies must be arrays.");
|
|
934
993
|
}
|
|
935
|
-
for (const filter of [...
|
|
994
|
+
for (const filter of [...request2.topicPolicy.publish, ...request2.topicPolicy.subscribe]) {
|
|
936
995
|
if (typeof filter !== "string" || filter.length === 0 || filter.length > 1024) {
|
|
937
996
|
throw new import_common6.BadRequestException("MQTT topic filters must be non-empty strings no longer than 1024 characters.");
|
|
938
997
|
}
|