Files
BetterDesk/web-nodejs/tests/middleware.auth.test.js
T
UNITRONIX bb3da6b801 feat: implement RdClient login functionality and enhance authentication flow
- Added a dedicated login page for the Remote Desktop client, allowing operators to sign in when their session expires.
- Implemented safe return URL handling for the login process.
- Updated authentication middleware to support RdClient-specific routes and permissions.
- Enhanced localization support for the new login feature across multiple languages.
- Improved user experience by redirecting to the appropriate pages based on authentication status.
2026-06-14 17:28:59 +02:00

215 lines
7.4 KiB
JavaScript

/**
* BetterDesk Console - Auth Middleware Tests
*/
const request = require('supertest');
const { createTestApp } = require('./helpers');
const { requireAuth, requireRole, guestOnly, requireRdClientAuth, rdClientGuestOnly, isSafeRdClientReturnUrl } = require('../middleware/auth');
describe('Auth Middleware', () => {
describe('requireAuth', () => {
it('should return 401 for unauthenticated API requests', async () => {
const app = createTestApp();
app.get('/api/test', requireAuth, (_req, res) => {
res.json({ success: true });
});
const res = await request(app).get('/api/test');
expect(res.status).toBe(401);
expect(res.body.success).toBe(false);
});
it('should redirect to login for unauthenticated HTML requests', async () => {
const app = createTestApp();
app.get('/dashboard', requireAuth, (_req, res) => {
res.send('OK');
});
const res = await request(app).get('/dashboard');
expect(res.status).toBe(302);
expect(res.headers.location).toBe('/login');
});
it('should pass through for authenticated requests', async () => {
const app = createTestApp();
app.use((req, _res, next) => {
req.session.userId = 1;
req.session.user = { id: 1, username: 'admin', role: 'admin' };
next();
});
app.get('/api/test', requireAuth, (_req, res) => {
res.json({ success: true });
});
const res = await request(app).get('/api/test');
expect(res.status).toBe(200);
expect(res.body.success).toBe(true);
});
});
describe('requireRole', () => {
it('should allow admin access to admin-only routes', async () => {
const app = createTestApp();
app.use((req, _res, next) => {
req.session.userId = 1;
req.session.user = { id: 1, username: 'admin', role: 'admin' };
next();
});
app.get('/api/admin', requireRole('admin'), (_req, res) => {
res.json({ success: true });
});
const res = await request(app).get('/api/admin');
expect(res.status).toBe(200);
});
it('should deny operator access to admin-only routes', async () => {
const app = createTestApp();
app.use((req, _res, next) => {
req.session.userId = 2;
req.session.user = { id: 2, username: 'operator1', role: 'operator' };
next();
});
app.get('/api/admin', requireRole('admin'), (_req, res) => {
res.json({ success: true });
});
const res = await request(app).get('/api/admin');
expect(res.status).toBe(403);
});
it('should allow admin to access operator routes', async () => {
const app = createTestApp();
app.use((req, _res, next) => {
req.session.userId = 1;
req.session.user = { id: 1, username: 'admin', role: 'admin' };
next();
});
app.get('/api/op', requireRole('operator'), (_req, res) => {
res.json({ success: true });
});
const res = await request(app).get('/api/op');
expect(res.status).toBe(200);
});
it('should return 401 for unauthenticated API requests', async () => {
const app = createTestApp();
app.get('/api/admin', requireRole('admin'), (_req, res) => {
res.json({ success: true });
});
const res = await request(app).get('/api/admin');
expect(res.status).toBe(401);
});
});
describe('guestOnly', () => {
it('should allow unauthenticated users', async () => {
const app = createTestApp();
app.get('/login', guestOnly, (_req, res) => {
res.send('login page');
});
const res = await request(app).get('/login');
expect(res.status).toBe(200);
});
it('should redirect authenticated users to dashboard', async () => {
const app = createTestApp();
app.use((req, _res, next) => {
req.session.userId = 1;
req.session.user = { id: 1, username: 'admin', role: 'admin' };
next();
});
app.get('/login', guestOnly, (_req, res) => {
res.send('login page');
});
const res = await request(app).get('/login');
expect(res.status).toBe(302);
expect(res.headers.location).toBe('/');
});
});
describe('requireRdClientAuth', () => {
it('should redirect unauthenticated HTML requests to /remote/login', async () => {
const app = createTestApp();
app.get('/remote', requireRdClientAuth('device.connect'), (_req, res) => {
res.send('OK');
});
const res = await request(app).get('/remote');
expect(res.status).toBe(302);
expect(res.headers.location).toBe('/remote/login?return=%2Fremote');
});
it('should return 401 for unauthenticated API requests', async () => {
const app = createTestApp();
app.get('/api/remote/sessions', requireRdClientAuth(), (_req, res) => {
res.json({ sessions: [] });
});
const res = await request(app).get('/api/remote/sessions');
expect(res.status).toBe(401);
});
it('should pass through for authenticated operator with permission', async () => {
const app = createTestApp();
app.use((req, _res, next) => {
req.session.userId = 2;
req.session.user = { id: 2, username: 'operator1', role: 'operator' };
next();
});
app.get('/remote', requireRdClientAuth('device.connect'), (_req, res) => {
res.send('OK');
});
const res = await request(app).get('/remote');
expect(res.status).toBe(200);
});
});
describe('isSafeRdClientReturnUrl', () => {
it('should accept /remote paths only', () => {
expect(isSafeRdClientReturnUrl('/remote')).toBe(true);
expect(isSafeRdClientReturnUrl('/remote/abc123')).toBe(true);
expect(isSafeRdClientReturnUrl('/login')).toBe(false);
expect(isSafeRdClientReturnUrl('/remote/login')).toBe(false);
expect(isSafeRdClientReturnUrl('//evil.com/remote')).toBe(false);
});
});
describe('rdClientGuestOnly', () => {
it('should redirect authenticated operator to return URL', async () => {
const app = createTestApp();
app.use((req, _res, next) => {
req.session.userId = 2;
req.session.user = { id: 2, username: 'operator1', role: 'operator' };
next();
});
app.get('/remote/login', rdClientGuestOnly, (_req, res) => {
res.send('login');
});
const res = await request(app).get('/remote/login?return=%2Fremote%2Fdev1');
expect(res.status).toBe(302);
expect(res.headers.location).toBe('/remote/dev1');
});
});
});