feat: generate authenticated url (#1471)

* refactor: extract info component

* feat: generate authenticated url

* refactor: add companion select

* refactor: ensure behaviour across environments
This commit is contained in:
Carlos Valente
2025-01-26 16:51:28 +01:00
committed by GitHub
parent d34280c03d
commit 0f57689750
19 changed files with 305 additions and 44 deletions
@@ -1,5 +1,5 @@
import { getErrorMessage } from 'ontime-utils';
import { ErrorResponse, GetInfo, SessionStats } from 'ontime-types';
import { ErrorResponse, GetInfo, GetUrl, SessionStats } from 'ontime-types';
import type { Request, Response } from 'express';
@@ -24,3 +24,18 @@ export async function getInfo(_req: Request, res: Response<GetInfo | ErrorRespon
res.status(500).send({ message });
}
}
export async function generateUrl(req: Request, res: Response<GetUrl | ErrorResponse>) {
try {
const url = sessionService.generateAuthenticatedUrl(
req.body.baseUrl,
req.body.path,
req.body.lock,
req.body.authenticate,
);
res.status(200).send({ url: url.toString() });
} catch (error) {
const message = getErrorMessage(error);
res.status(500).send({ message });
}
}
@@ -1,8 +1,10 @@
import express from 'express';
import { getInfo, getSessionStats } from './session.controller.js';
import { getInfo, getSessionStats, generateUrl } from './session.controller.js';
import { validateGenerateUrl } from './session.validation.js';
export const router = express.Router();
router.get('/', getSessionStats);
router.get('/info', getInfo);
router.post('/url', validateGenerateUrl, generateUrl);
@@ -8,6 +8,8 @@ import { getLastLoadedProject } from '../../services/app-state-service/AppStateS
import { runtimeService } from '../../services/runtime-service/RuntimeService.js';
import { getNetworkInterfaces } from '../../utils/network.js';
import { getTimezoneLabel } from '../../utils/time.js';
import { password } from '../../externals.js';
import { hashPassword } from '../../utils/hash.js';
const startedAt = new Date();
@@ -46,3 +48,20 @@ export async function getInfo(): Promise<GetInfo> {
publicDir: publicDir.root,
};
}
export const hasPassword = Boolean(password);
export const hashedPassword = hasPassword ? hashPassword(password as string) : undefined;
/**
* Generates a pre-authenticated URL by injecting a token in the URL params
*/
export function generateAuthenticatedUrl(baseUrl: string, path: string, lock: boolean, authenticate: boolean): URL {
const url = new URL(path, baseUrl);
if (authenticate && hashedPassword) {
url.searchParams.append('token', hashedPassword);
}
if (lock) {
url.searchParams.append('locked', 'true');
}
return url;
}
@@ -0,0 +1,15 @@
import type { Request, Response, NextFunction } from 'express';
import { body, validationResult } from 'express-validator';
export const validateGenerateUrl = [
body('baseUrl').exists().isString().notEmpty().trim(),
body('path').exists().isString().trim(),
body('lock').exists().isBoolean(),
body('authenticate').exists().isBoolean(),
(req: Request, res: Response, next: NextFunction) => {
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
];