mirror of
https://github.com/UNITRONIX/BetterDesk.git
synced 2026-09-11 01:55:43 +00:00
bb3da6b801
- 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.
215 lines
7.4 KiB
JavaScript
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');
|
|
});
|
|
});
|
|
});
|