const test = require('node:test'); const assert = require('node:assert/strict'); const path = require('path'); const registerMiddleware = require('../src/web/middleware'); test('web middleware bypasses auth for internal player sync routes', () => { const calls = []; const app = { middlewares: [], use(...args) { this.middlewares.push(args); } }; registerMiddleware(app, { pool: {}, loadCurrentUser: async () => null, requireAuth(req, _res, next) { calls.push(req.path); next(); }, MEDIA_DIR: path.join(process.cwd(), 'media'), UPLOADS_DIR: path.join(process.cwd(), 'media', 'uploads-test'), THUMBNAILS_DIR: path.join(process.cwd(), 'media', 'thumbnails-test'), ASSET_DIR: path.join(process.cwd(), 'src', 'web', 'public') }); const authGate = app.middlewares[app.middlewares.length - 1][0]; const nextCalls = []; authGate({ path: '/api/internal/sync/player-media' }, {}, function () { nextCalls.push('media'); }); authGate({ path: '/api/internal/sync/player-font' }, {}, function () { nextCalls.push('font'); }); authGate({ path: '/dashboard' }, {}, function () { nextCalls.push('dashboard'); }); assert.deepEqual(nextCalls, ['media', 'font', 'dashboard']); assert.deepEqual(calls, ['/dashboard']); }); test('view-as blocks writes but allows the stop-preview request', () => { const app = { middlewares: [], use(...args) { this.middlewares.push(args); } }; registerMiddleware(app, { pool: {}, loadCurrentUser: async () => null, requireAuth(req, _res, next) { next(); }, MEDIA_DIR: path.join(process.cwd(), 'media'), UPLOADS_DIR: path.join(process.cwd(), 'media', 'uploads-test'), THUMBNAILS_DIR: path.join(process.cwd(), 'media', 'thumbnails-test'), ASSET_DIR: path.join(process.cwd(), 'src', 'web', 'public') }); const authGate = app.middlewares[app.middlewares.length - 1][0]; const errors = []; let stopAllowed = false; ['/slides/12', '/api/internal/sync/player-control'].forEach(function (requestPath) { authGate({ method: 'POST', path: requestPath, currentUser: { id: 19, isViewingAs: true }, authenticatedUser: { id: 7 } }, {}, function (error) { errors.push(error); }); }); authGate({ method: 'POST', path: '/settings/view-as/stop', currentUser: { id: 19, isViewingAs: true }, authenticatedUser: { id: 7 } }, {}, function (error) { stopAllowed = !error; }); assert.equal(errors.length, 2); assert.equal(errors[0].statusCode, 403); assert.equal(stopAllowed, true); }); test('view-as ignores the preview user password-change requirement and permits exit', () => { const app = { middlewares: [], use(...args) { this.middlewares.push(args); } }; registerMiddleware(app, { pool: {}, loadCurrentUser: async () => null, requireAuth(_req, _res, next) { next(); }, MEDIA_DIR: path.join(process.cwd(), 'media'), UPLOADS_DIR: path.join(process.cwd(), 'media', 'uploads-test'), THUMBNAILS_DIR: path.join(process.cwd(), 'media', 'thumbnails-test'), ASSET_DIR: path.join(process.cwd(), 'src', 'web', 'public') }); const authGate = app.middlewares[app.middlewares.length - 1][0]; let pageAllowed = false; const response = { redirect(path) { this.redirectedTo = path; } }; authGate({ method: 'GET', path: '/screens', currentUser: { id: 19, isViewingAs: true, mustChangePassword: true }, authenticatedUser: { id: 7 } }, response, function (error) { assert.ifError(error); pageAllowed = true; }); assert.equal(pageAllowed, true); assert.equal(response.redirectedTo, undefined); const adminMustChangeResponse = { redirect(path) { this.redirectedTo = path; } }; authGate({ method: 'GET', path: '/screens', currentUser: { id: 19, isViewingAs: true, mustChangePassword: false }, authenticatedUser: { id: 7, mustChangePassword: true } }, adminMustChangeResponse, function (error) { assert.ifError(error); }); assert.equal(adminMustChangeResponse.redirectedTo, '/account/password'); let stopAllowed = false; authGate({ method: 'POST', path: '/settings/view-as/stop', currentUser: { id: 19, isViewingAs: true, mustChangePassword: true }, authenticatedUser: { id: 7, mustChangePassword: true } }, {}, function (error) { assert.ifError(error); stopAllowed = true; }); assert.equal(stopAllowed, true); }); test('required-password gate blocks account details and allows only password page and logout', () => { const app = { middlewares: [], use(...args) { this.middlewares.push(args); } }; registerMiddleware(app, { pool: {}, loadCurrentUser: async () => null, requireAuth(_req, _res, next) { next(); }, MEDIA_DIR: path.join(process.cwd(), 'media'), UPLOADS_DIR: path.join(process.cwd(), 'media', 'uploads-test'), THUMBNAILS_DIR: path.join(process.cwd(), 'media', 'thumbnails-test'), ASSET_DIR: path.join(process.cwd(), 'src', 'web', 'public') }); const authGate = app.middlewares[app.middlewares.length - 1][0]; const requests = [ { method: 'GET', path: '/account' }, { method: 'POST', path: '/account/profile' }, { method: 'GET', path: '/screens' } ]; requests.forEach(function (req) { const response = { redirect(path) { this.redirectedTo = path; } }; Object.assign(req, { currentUser: { id: 5, mustChangePassword: true }, authenticatedUser: { id: 5, mustChangePassword: true } }); authGate(req, response, function (error) { assert.ifError(error); }); assert.equal(response.redirectedTo, '/account/password'); }); const passwordPageResponse = {}; authGate({ method: 'GET', path: '/account/password', currentUser: { id: 5, mustChangePassword: true }, authenticatedUser: { id: 5, mustChangePassword: true } }, passwordPageResponse, function (error) { assert.ifError(error); }); const logoutResponse = {}; authGate({ method: 'GET', path: '/logout', currentUser: { id: 5, mustChangePassword: true }, authenticatedUser: { id: 5, mustChangePassword: true } }, logoutResponse, function (error) { assert.ifError(error); }); });