carta-controller 5.1.1 → 6.0.0-beta.1.0.3
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/.github/workflows/build.yml +43 -0
- package/COPYING.md +636 -0
- package/biome.jsonc +37 -0
- package/dist/auth/external.js +10 -4
- package/dist/auth/external.js.map +1 -1
- package/dist/auth/google.js +18 -11
- package/dist/auth/google.js.map +1 -1
- package/dist/auth/index.js +12 -12
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/ldap.js +6 -3
- package/dist/auth/ldap.js.map +1 -1
- package/dist/auth/local.js +30 -14
- package/dist/auth/local.js.map +1 -1
- package/dist/auth/oidc.js +95 -91
- package/dist/auth/oidc.js.map +1 -1
- package/dist/auth/oidcRefreshManager.js +21 -24
- package/dist/auth/oidcRefreshManager.js.map +1 -1
- package/dist/auth/pam.js +8 -5
- package/dist/auth/pam.js.map +1 -1
- package/dist/config.js +17 -16
- package/dist/controllerTests.js +10 -10
- package/dist/database.js +50 -22
- package/dist/index.js +24 -23
- package/dist/serverHandlers.js +70 -33
- package/dist/util.js +14 -5
- package/npm-shrinkwrap.json +4855 -20113
- package/package.json +12 -9
- package/public/dashboard.js +47 -48
- package/public/templated.css +155 -143
- package/test/auth.external.test.ts +19 -18
- package/.prettierrc.json +0 -18
package/dist/serverHandlers.js
CHANGED
|
@@ -46,19 +46,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
46
46
|
};
|
|
47
47
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
48
|
exports.serverRouter = exports.createScriptingProxyHandler = exports.createUpgradeHandler = void 0;
|
|
49
|
+
const node_child_process_1 = require("node:child_process");
|
|
50
|
+
const fs = __importStar(require("node:fs"));
|
|
51
|
+
const querystring = __importStar(require("node:querystring"));
|
|
52
|
+
const url = __importStar(require("node:url"));
|
|
53
|
+
const io_1 = __importDefault(require("@pm2/io"));
|
|
49
54
|
const express_1 = __importDefault(require("express"));
|
|
50
|
-
const
|
|
51
|
-
const fs = __importStar(require("fs"));
|
|
55
|
+
const mnemonist_1 = require("mnemonist");
|
|
52
56
|
const moment_1 = __importDefault(require("moment"));
|
|
53
|
-
const querystring = __importStar(require("querystring"));
|
|
54
|
-
const uuid_1 = require("uuid");
|
|
55
|
-
const io_1 = __importDefault(require("@pm2/io"));
|
|
56
57
|
const tcpPortUsed = __importStar(require("tcp-port-used"));
|
|
57
|
-
const
|
|
58
|
-
const mnemonist_1 = require("mnemonist");
|
|
59
|
-
const util_1 = require("./util");
|
|
58
|
+
const uuid_1 = require("uuid");
|
|
60
59
|
const auth_1 = require("./auth");
|
|
61
60
|
const config_1 = require("./config");
|
|
61
|
+
const util_1 = require("./util");
|
|
62
62
|
const processMap = new Map();
|
|
63
63
|
const logMap = new Map();
|
|
64
64
|
const LOG_LIMIT = 1000;
|
|
@@ -101,7 +101,7 @@ function deleteProcess(username) {
|
|
|
101
101
|
function nextAvailablePort() {
|
|
102
102
|
return __awaiter(this, void 0, void 0, function* () {
|
|
103
103
|
// Get a map of all the ports in the range currently in use
|
|
104
|
-
|
|
104
|
+
const existingPorts = new Map();
|
|
105
105
|
processMap.forEach(value => {
|
|
106
106
|
existingPorts.set(value.port, true);
|
|
107
107
|
});
|
|
@@ -176,7 +176,7 @@ function handleStartServer(req, res, next) {
|
|
|
176
176
|
const existingProcess = processMap.get(username);
|
|
177
177
|
if (existingProcess) {
|
|
178
178
|
// Kill the process via the kill script
|
|
179
|
-
(0,
|
|
179
|
+
(0, node_child_process_1.spawnSync)("sudo", ["-u", `${username}`, config_1.ServerConfig.killCommand, `${existingProcess.process.pid}`]);
|
|
180
180
|
// Delay to allow the parent process to exit
|
|
181
181
|
yield (0, util_1.delay)(10);
|
|
182
182
|
deleteProcess(username);
|
|
@@ -185,7 +185,10 @@ function handleStartServer(req, res, next) {
|
|
|
185
185
|
catch (e) {
|
|
186
186
|
util_1.logger.debug(e);
|
|
187
187
|
util_1.logger.error(`Error killing existing process belonging to user ${username}`);
|
|
188
|
-
return next({
|
|
188
|
+
return next({
|
|
189
|
+
statusCode: 400,
|
|
190
|
+
message: "Problem killing existing process"
|
|
191
|
+
});
|
|
189
192
|
}
|
|
190
193
|
}
|
|
191
194
|
else {
|
|
@@ -210,7 +213,10 @@ function startServer(username) {
|
|
|
210
213
|
try {
|
|
211
214
|
const port = yield nextAvailablePort();
|
|
212
215
|
if (port < 0) {
|
|
213
|
-
throw {
|
|
216
|
+
throw {
|
|
217
|
+
statusCode: 500,
|
|
218
|
+
message: "No available ports for the backend process"
|
|
219
|
+
};
|
|
214
220
|
}
|
|
215
221
|
let args = [];
|
|
216
222
|
if (config_1.ServerConfig.preserveEnv) {
|
|
@@ -238,9 +244,14 @@ function startServer(username) {
|
|
|
238
244
|
// Finally, add the positional argument for the base folder
|
|
239
245
|
args.push(config_1.ServerConfig.baseFolderTemplate.replace("{username}", username));
|
|
240
246
|
const headerToken = (0, uuid_1.v4)();
|
|
241
|
-
const child = (0,
|
|
242
|
-
|
|
243
|
-
|
|
247
|
+
const child = (0, node_child_process_1.spawn)("sudo", args, {
|
|
248
|
+
env: { CARTA_AUTH_TOKEN: headerToken }
|
|
249
|
+
});
|
|
250
|
+
if ((child === null || child === void 0 ? void 0 : child.pid) == null) {
|
|
251
|
+
throw {
|
|
252
|
+
statusCode: 500,
|
|
253
|
+
message: `Problem starting process for user ${username}`
|
|
254
|
+
};
|
|
244
255
|
}
|
|
245
256
|
setPendingProcess(username, port, headerToken, child);
|
|
246
257
|
let logLocation;
|
|
@@ -248,16 +259,16 @@ function startServer(username) {
|
|
|
248
259
|
logLocation = config_1.ServerConfig.backendLogFileTemplate.replace("{username}", username).replace("{pid}", child.pid.toString()).replace("{datetime}", (0, moment_1.default)().format("YYYYMMDD.h_mm_ss"));
|
|
249
260
|
try {
|
|
250
261
|
logStream = fs.createWriteStream(logLocation, { flags: "a" });
|
|
251
|
-
if (logStream ==
|
|
262
|
+
if (logStream == null) {
|
|
252
263
|
throw new Error("Unable to open stream");
|
|
253
264
|
}
|
|
254
265
|
child.stdout.pipe(logStream);
|
|
255
266
|
child.stderr.pipe(logStream);
|
|
256
|
-
child.stdout.on("data",
|
|
267
|
+
child.stdout.on("data", data => {
|
|
257
268
|
const line = data.toString();
|
|
258
269
|
appendLog(username, line);
|
|
259
270
|
});
|
|
260
|
-
child.stderr.on("data",
|
|
271
|
+
child.stderr.on("data", data => {
|
|
261
272
|
const line = data.toString();
|
|
262
273
|
appendLog(username, line);
|
|
263
274
|
});
|
|
@@ -269,12 +280,12 @@ function startServer(username) {
|
|
|
269
280
|
}
|
|
270
281
|
else {
|
|
271
282
|
logLocation = "stdout";
|
|
272
|
-
child.stdout.on("data",
|
|
283
|
+
child.stdout.on("data", data => {
|
|
273
284
|
const line = data.toString();
|
|
274
285
|
appendLog(username, line);
|
|
275
286
|
util_1.logger.info(line);
|
|
276
287
|
});
|
|
277
|
-
child.stderr.on("data",
|
|
288
|
+
child.stderr.on("data", data => {
|
|
278
289
|
const line = data.toString();
|
|
279
290
|
appendLog(username, line);
|
|
280
291
|
util_1.logger.error(line);
|
|
@@ -288,7 +299,10 @@ function startServer(username) {
|
|
|
288
299
|
// Check for early exit of backend process
|
|
289
300
|
yield (0, util_1.delay)(config_1.ServerConfig.startDelay);
|
|
290
301
|
if (child.exitCode || child.signalCode) {
|
|
291
|
-
throw {
|
|
302
|
+
throw {
|
|
303
|
+
statusCode: 500,
|
|
304
|
+
message: `Problem starting process for user ${username}`
|
|
305
|
+
};
|
|
292
306
|
}
|
|
293
307
|
else {
|
|
294
308
|
util_1.logger.info(`Started process with PID ${child.pid} for user ${username} on port ${port}. Outputting to ${logLocation}`);
|
|
@@ -304,7 +318,10 @@ function startServer(username) {
|
|
|
304
318
|
throw e;
|
|
305
319
|
}
|
|
306
320
|
else {
|
|
307
|
-
throw {
|
|
321
|
+
throw {
|
|
322
|
+
statusCode: 500,
|
|
323
|
+
message: `Problem starting process for user ${username}`
|
|
324
|
+
};
|
|
308
325
|
}
|
|
309
326
|
}
|
|
310
327
|
});
|
|
@@ -320,7 +337,7 @@ function handleStopServer(req, res, next) {
|
|
|
320
337
|
if (existingProcess) {
|
|
321
338
|
existingProcess.process.removeAllListeners();
|
|
322
339
|
// Kill the process via the kill script
|
|
323
|
-
(0,
|
|
340
|
+
(0, node_child_process_1.spawnSync)("sudo", ["-u", `${req.username}`, config_1.ServerConfig.killCommand, `${existingProcess.process.pid}`]);
|
|
324
341
|
// Delay to allow the parent process to exit
|
|
325
342
|
yield (0, util_1.delay)(10);
|
|
326
343
|
util_1.logger.info(`Process with PID ${existingProcess.process.pid} for user ${req.username} exited via stop request`);
|
|
@@ -328,13 +345,19 @@ function handleStopServer(req, res, next) {
|
|
|
328
345
|
res.json({ success: true });
|
|
329
346
|
}
|
|
330
347
|
else {
|
|
331
|
-
return next({
|
|
348
|
+
return next({
|
|
349
|
+
statusCode: 400,
|
|
350
|
+
message: `No existing process belonging to user ${req.username}`
|
|
351
|
+
});
|
|
332
352
|
}
|
|
333
353
|
}
|
|
334
354
|
catch (e) {
|
|
335
355
|
util_1.logger.debug(e);
|
|
336
356
|
util_1.logger.error(`Error killing existing process belonging to user ${req.username}`);
|
|
337
|
-
return next({
|
|
357
|
+
return next({
|
|
358
|
+
statusCode: 500,
|
|
359
|
+
message: "Problem killing existing process"
|
|
360
|
+
});
|
|
338
361
|
}
|
|
339
362
|
});
|
|
340
363
|
}
|
|
@@ -344,12 +367,12 @@ const createUpgradeHandler = (server) => (req, socket, head) => __awaiter(void 0
|
|
|
344
367
|
if (!(req === null || req === void 0 ? void 0 : req.url)) {
|
|
345
368
|
return socket.end();
|
|
346
369
|
}
|
|
347
|
-
|
|
370
|
+
const parsedUrl = url.parse(req.url);
|
|
348
371
|
if (!(parsedUrl === null || parsedUrl === void 0 ? void 0 : parsedUrl.query)) {
|
|
349
372
|
util_1.logger.warning(`Incoming Websocket upgrade request could not be parsed: ${req.url}`);
|
|
350
373
|
return socket.end();
|
|
351
374
|
}
|
|
352
|
-
|
|
375
|
+
const queryParameters = querystring.parse(parsedUrl.query);
|
|
353
376
|
const tokenString = queryParameters === null || queryParameters === void 0 ? void 0 : queryParameters.token;
|
|
354
377
|
if (!tokenString || Array.isArray(tokenString)) {
|
|
355
378
|
util_1.logger.warning(`Incoming Websocket upgrade request is missing an authentication token`);
|
|
@@ -362,7 +385,7 @@ const createUpgradeHandler = (server) => (req, socket, head) => __awaiter(void 0
|
|
|
362
385
|
}
|
|
363
386
|
const remoteAddress = ((_a = req.headers) === null || _a === void 0 ? void 0 : _a["x-forwarded-for"]) || ((_b = req.connection) === null || _b === void 0 ? void 0 : _b.remoteAddress);
|
|
364
387
|
util_1.logger.info(`WS upgrade request from ${remoteAddress} for authenticated user ${token.username}`);
|
|
365
|
-
const username = (0, auth_1.getUser)(token.username, token.iss);
|
|
388
|
+
const username = (0, auth_1.getUser)(token.username, `${token.iss}`);
|
|
366
389
|
if (!username) {
|
|
367
390
|
util_1.logger.error(`Could not find username ${token.username} in the user map`);
|
|
368
391
|
return socket.end();
|
|
@@ -382,7 +405,9 @@ const createUpgradeHandler = (server) => (req, socket, head) => __awaiter(void 0
|
|
|
382
405
|
util_1.logger.info(`Redirecting to backend process for ${username} (port ${existingProcess.port})`);
|
|
383
406
|
req.headers["carta-auth-token"] = existingProcess.headerToken;
|
|
384
407
|
req.url = "/";
|
|
385
|
-
return server.ws(req, socket, head, {
|
|
408
|
+
return server.ws(req, socket, head, {
|
|
409
|
+
target: { host: "localhost", port: existingProcess.port }
|
|
410
|
+
});
|
|
386
411
|
}
|
|
387
412
|
else {
|
|
388
413
|
util_1.logger.error(`Backend process could not be started`);
|
|
@@ -403,10 +428,14 @@ const createScriptingProxyHandler = (server) => (req, res, next) => __awaiter(vo
|
|
|
403
428
|
return next({ statusCode: 401, message: "Not authorized" });
|
|
404
429
|
}
|
|
405
430
|
if (!req.scripting) {
|
|
406
|
-
return next({
|
|
431
|
+
return next({
|
|
432
|
+
statusCode: 403,
|
|
433
|
+
message: "API token supplied does not permit scripting"
|
|
434
|
+
});
|
|
407
435
|
}
|
|
408
436
|
try {
|
|
409
437
|
const remoteAddress = ((_a = req.headers) === null || _a === void 0 ? void 0 : _a["x-forwarded-for"]) || ((_b = req.connection) === null || _b === void 0 ? void 0 : _b.remoteAddress);
|
|
438
|
+
util_1.logger.info(`Scripting proxy request from ${remoteAddress} for authenticated user ${username}`);
|
|
410
439
|
let existingProcess = processMap.get(username);
|
|
411
440
|
if (!(existingProcess === null || existingProcess === void 0 ? void 0 : existingProcess.process) || existingProcess.process.signalCode) {
|
|
412
441
|
// Attempt to start new process
|
|
@@ -420,16 +449,24 @@ const createScriptingProxyHandler = (server) => (req, res, next) => __awaiter(vo
|
|
|
420
449
|
yield (0, util_1.delay)(config_1.ServerConfig.startDelay);
|
|
421
450
|
}
|
|
422
451
|
req.headers["carta-auth-token"] = existingProcess.headerToken;
|
|
423
|
-
return server.web(req, res, {
|
|
452
|
+
return server.web(req, res, {
|
|
453
|
+
target: { host: "localhost", port: existingProcess.port }
|
|
454
|
+
});
|
|
424
455
|
}
|
|
425
456
|
else {
|
|
426
|
-
return next({
|
|
457
|
+
return next({
|
|
458
|
+
statusCode: 500,
|
|
459
|
+
message: `Backend process could not be started for ${username}`
|
|
460
|
+
});
|
|
427
461
|
}
|
|
428
462
|
}
|
|
429
463
|
catch (err) {
|
|
430
464
|
util_1.logger.error(`Error proxying scripting request for ${req.username}`);
|
|
431
465
|
util_1.logger.debug(err);
|
|
432
|
-
return next({
|
|
466
|
+
return next({
|
|
467
|
+
statusCode: 500,
|
|
468
|
+
message: `Error proxying scripting request for ${req.username}`
|
|
469
|
+
});
|
|
433
470
|
}
|
|
434
471
|
});
|
|
435
472
|
exports.createScriptingProxyHandler = createScriptingProxyHandler;
|
package/dist/util.js
CHANGED
|
@@ -16,11 +16,12 @@ exports.logger = void 0;
|
|
|
16
16
|
exports.delay = delay;
|
|
17
17
|
exports.noCache = noCache;
|
|
18
18
|
exports.getUserId = getUserId;
|
|
19
|
-
|
|
19
|
+
exports.generateUrlSafeString = generateUrlSafeString;
|
|
20
|
+
const node_child_process_1 = require("node:child_process");
|
|
20
21
|
const winston_1 = __importDefault(require("winston"));
|
|
21
22
|
exports.logger = winston_1.default.createLogger({
|
|
22
23
|
// Detailed setup is completed in config.ts
|
|
23
|
-
levels: winston_1.default.config.syslog.levels
|
|
24
|
+
levels: winston_1.default.config.syslog.levels
|
|
24
25
|
});
|
|
25
26
|
// Delay for the specified number of milliseconds
|
|
26
27
|
function delay(delay) {
|
|
@@ -30,7 +31,7 @@ function delay(delay) {
|
|
|
30
31
|
});
|
|
31
32
|
});
|
|
32
33
|
}
|
|
33
|
-
function noCache(
|
|
34
|
+
function noCache(_req, res, next) {
|
|
34
35
|
res.header("Cache-Control", "private, no-cache, no-store, must-revalidate");
|
|
35
36
|
res.header("Expires", "-1");
|
|
36
37
|
res.header("Pragma", "no-cache");
|
|
@@ -40,13 +41,21 @@ function getUserId(username) {
|
|
|
40
41
|
if (!username) {
|
|
41
42
|
throw new Error("Missing argument for username");
|
|
42
43
|
}
|
|
43
|
-
const result = (0,
|
|
44
|
+
const result = (0, node_child_process_1.spawnSync)("id", ["-u", username]);
|
|
44
45
|
if (!result.status && (result === null || result === void 0 ? void 0 : result.stdout)) {
|
|
45
46
|
const uid = Number.parseInt(result.stdout.toString());
|
|
46
|
-
if (isFinite(uid)) {
|
|
47
|
+
if (Number.isFinite(uid)) {
|
|
47
48
|
return uid;
|
|
48
49
|
}
|
|
49
50
|
}
|
|
50
51
|
throw new Error(`Can't find uid for username ${username}`);
|
|
51
52
|
}
|
|
53
|
+
function generateUrlSafeString(length) {
|
|
54
|
+
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~";
|
|
55
|
+
let sessionId = "";
|
|
56
|
+
for (let i = 0; i < length; i++) {
|
|
57
|
+
sessionId += charset[Math.floor(Math.random() * charset.length)];
|
|
58
|
+
}
|
|
59
|
+
return sessionId;
|
|
60
|
+
}
|
|
52
61
|
//# sourceMappingURL=util.js.map
|