Files
pulse-signage/test/auth-routes.test.js
T
lzstealth 203d0bfc01
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile, web, pulse-signage-web) (push) Successful in 1m49s
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile.player, player, pulse-signage-player) (push) Successful in 32s
Release 2.8.0
2026-08-16 17:15:31 +01:00

160 lines
4.1 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert/strict');
require('../src/common');
const registerAuthRoutes = require('../src/web/routes/auth');
const renderLoginPage = require('../src/web/routes/auth/login');
const { createSessionService, normalizeReturnToPath } = require('../src/web/lib/auth/session');
test('login page preserves the return target in a hidden field', () => {
const html = renderLoginPage('', '/slides/6/edit', 'demo');
assert.match(html, /name="returnTo"/);
assert.match(html, /value="\/slides\/6\/edit"/);
assert.match(html, /name="remember_me"/);
assert.match(html, /name="username"[^>]*value="demo"/);
});
test('requireAuth preserves the original request path when redirecting to login', () => {
const sessionService = createSessionService({
sessionCookieName: 'pulse_session',
sessionMaxAgeMs: 60 * 60 * 1000,
hashSessionToken: (value) => value,
createSessionToken: () => 'token'
});
const req = {
originalUrl: '/slides/6/edit?tab=content'
};
const res = {
headers: {},
getHeader(name) {
return this.headers[name];
},
setHeader(name, value) {
this.headers[name] = value;
},
redirect(url) {
this.redirectedTo = url;
}
};
sessionService.requireAuth(req, res, () => {});
assert.equal(res.redirectedTo, '/login?returnTo=%2Fslides%2F6%2Fedit%3Ftab%3Dcontent');
});
test('normalizeReturnToPath keeps same-origin absolute urls and strips the origin', () => {
assert.equal(
normalizeReturnToPath('https://signage.example.com/slides/6/edit?tab=content', 'https://signage.example.com'),
'/slides/6/edit?tab=content'
);
assert.equal(
normalizeReturnToPath('https://evil.example.com/slides/6/edit', 'https://signage.example.com'),
''
);
});
test('successful login returns to the requested link instead of the dashboard', async () => {
const handlers = {};
const app = {
get(path, ...routeHandlers) {
handlers['GET ' + path] = routeHandlers;
},
post(path, ...routeHandlers) {
handlers['POST ' + path] = routeHandlers;
}
};
registerAuthRoutes(app, {
pool: {
async query(sql) {
if (sql.includes('FROM a_users WHERE username = ? LIMIT 1')) {
return [[{ id: 7, name: 'Demo User', username: 'demo', password_hash: 'hash', password_salt: 'salt', password_iterations: 1 }]];
}
return [[]];
}
},
pages: {
renderLoginPage(message, returnTo) {
return { message, returnTo };
}
},
createUserSession: async () => 'session-token',
getSessionMaxAgeMs: async () => 7 * 24 * 60 * 60 * 1000,
setSessionCookie(res, token, maxAgeMs) {
res.sessionToken = token;
res.sessionMaxAgeMs = maxAgeMs;
},
clearSessionCookie() {},
parseCookies() {
return {};
},
consumeAuthMessageCookie() {
return '';
},
setAuthMessageCookie() {},
hashSessionToken(value) {
return value;
},
verifyPassword() {
return true;
},
sessionCookieName: 'pulse_session'
});
const req = {
body: {
username: 'demo',
password: 'secret',
returnTo: '/slides/6/edit?tab=content'
},
query: {
returnTo: '/slides/6/edit?tab=content'
}
};
const res = {
redirect(url) {
this.redirectedTo = url;
},
status() {
return this;
},
send(value) {
this.sentValue = value;
return this;
}
};
await handlers['POST /login'][0](req, res, () => {});
assert.equal(res.sessionToken, 'session-token');
assert.equal(res.sessionMaxAgeMs, null);
assert.equal(res.redirectedTo, '/slides/6/edit?tab=content');
const rememberedRequest = {
body: {
username: 'demo',
password: 'secret',
remember_me: '1'
},
query: {}
};
const rememberedResponse = {
redirect(url) {
this.redirectedTo = url;
},
status() {
return this;
},
send(value) {
this.sentValue = value;
return this;
}
};
await handlers['POST /login'][0](rememberedRequest, rememberedResponse, () => {});
assert.equal(rememberedResponse.sessionMaxAgeMs, 7 * 24 * 60 * 60 * 1000);
});