server be & fe
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
import { randomBytes } from 'node:crypto';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import type { AccessKeyView } from '@cardy/shared';
|
||||
import { loadConfig } from '../src/config.js';
|
||||
import { SUPERADMIN, testApp } from './helpers.js';
|
||||
|
||||
const baseEnv = {
|
||||
KEY_ENCRYPTION_SECRET: randomBytes(32).toString('base64'),
|
||||
SUPERADMIN_EMAIL: 'Root@Example.com',
|
||||
};
|
||||
|
||||
describe('loadConfig', () => {
|
||||
it('applies defaults', () => {
|
||||
const config = loadConfig(baseEnv);
|
||||
expect(config).toMatchObject({ superadminEmail: 'root@example.com', superadminGroup: 'default', host: '127.0.0.1', port: 10002 });
|
||||
expect(config.publicApiUrl).toBeUndefined();
|
||||
expect(config.adminProxySecret).toBeUndefined();
|
||||
});
|
||||
|
||||
it('validates values', () => {
|
||||
expect(loadConfig({ ...baseEnv, PUBLIC_API_URL: 'https://dev.aggtaa.com/api/' }).publicApiUrl).toBe('https://dev.aggtaa.com/api');
|
||||
expect(() => loadConfig({ ...baseEnv, PUBLIC_API_URL: 'nope' })).toThrow();
|
||||
expect(() => loadConfig({ ...baseEnv, SUPERADMIN_EMAIL: '' })).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('reverse proxy integration', () => {
|
||||
const proxied = {
|
||||
'remote-email': SUPERADMIN,
|
||||
'x-cardy-admin': '1',
|
||||
'x-forwarded-proto': 'https',
|
||||
'x-forwarded-host': 'dev.aggtaa.com',
|
||||
'x-forwarded-prefix': '/api',
|
||||
};
|
||||
|
||||
it('derives the deep link server and cookie path from X-Forwarded-* headers', async () => {
|
||||
const { app } = await testApp({ publicApiUrl: undefined });
|
||||
const key = (await app.inject({ method: 'POST', url: `/admin/users/${SUPERADMIN}/keys`, headers: proxied, payload: {} })).json() as AccessKeyView;
|
||||
expect(key.deepLink).toBe(`cardy://login?server=${encodeURIComponent('https://dev.aggtaa.com/api')}&key=${key.key}`);
|
||||
|
||||
const me = await app.inject({ url: '/admin/me', headers: proxied });
|
||||
const cookie = String(me.headers['set-cookie']);
|
||||
expect(cookie).toMatch(/Path=\/api\/admin/);
|
||||
expect(cookie).toMatch(/Secure/);
|
||||
});
|
||||
|
||||
it('ignores a malformed X-Forwarded-Prefix', async () => {
|
||||
const { app } = await testApp({ publicApiUrl: undefined });
|
||||
const key = (
|
||||
await app.inject({
|
||||
method: 'POST',
|
||||
url: `/admin/users/${SUPERADMIN}/keys`,
|
||||
headers: { ...proxied, 'x-forwarded-prefix': '/api"><script>' },
|
||||
payload: {},
|
||||
})
|
||||
).json() as AccessKeyView;
|
||||
expect(key.deepLink).toContain(`server=${encodeURIComponent('https://dev.aggtaa.com')}&key=`);
|
||||
});
|
||||
|
||||
it('rejects admin requests without the proxy secret when configured', async () => {
|
||||
const { app } = await testApp({ adminProxySecret: 's3cret' });
|
||||
const headers = { 'remote-email': SUPERADMIN };
|
||||
expect((await app.inject({ url: '/admin/me', headers })).statusCode).toBe(401);
|
||||
expect((await app.inject({ url: '/admin/me', headers: { ...headers, 'x-cardy-proxy-secret': 'wrong' } })).statusCode).toBe(401);
|
||||
expect((await app.inject({ url: '/admin/me', headers: { ...headers, 'x-cardy-proxy-secret': 's3cret' } })).statusCode).toBe(200);
|
||||
});
|
||||
|
||||
it('does not answer CORS preflight for admin paths', async () => {
|
||||
const { app } = await testApp();
|
||||
const res = await app.inject({
|
||||
method: 'OPTIONS',
|
||||
url: '/admin/me',
|
||||
headers: { origin: 'https://evil.example', 'access-control-request-method': 'GET' },
|
||||
});
|
||||
expect(res.headers['access-control-allow-origin']).toBeUndefined();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user