Files
boc/aamos-ledger/tests/auth.test.mjs
T
Bernt bae705aa97 ARCHITECTURE: NFC roadmap, edge AI, audit logging
- Add NFC ePassport roadmap (ICAO 9303, eIDAS)
- Add TensorFlow.js edge face detection (BlazeFace)
- Add structured audit logger (GDPR-compliant)
- Risk scoring support

Part of KYC Apple Native UX v1.1.0
2026-06-29 16:24:48 +00:00

183 lines
6.1 KiB
JavaScript

/**
* ═══════════════════════════════════════════════════════════════════════════
* AAMOS Ledger — Auth & RBAC Tester
* ═══════════════════════════════════════════════════════════════════════════
*/
import { describe, it, before } from 'node:test';
import assert from 'node:assert';
import jwt from 'jsonwebtoken';
// Mocka miljövariabler före import
process.env.NODE_ENV = 'development';
process.env.AMOS_JWT_SECRET = 'test-secret-minst-32-tecken-lång-12345';
process.env.LEDGER_INTERNAL_TOKEN = 'test-internal-token-12345';
const auth = await import('../auth.mjs');
describe('Auth-konfiguration', () => {
it('ska validera korrekt med giltiga miljövariabler', () => {
assert.doesNotThrow(() => auth.validateAuthConfig());
});
});
describe('JWT-verifiering', () => {
const secret = process.env.AMOS_JWT_SECRET;
it('ska acceptera giltig HS256-token', async () => {
const token = jwt.sign({ sub: 'user-1', roles: ['viewer'] }, secret, { algorithm: 'HS256' });
const req = { headers: { authorization: `Bearer ${token}` } };
const res = { status: () => ({ json: () => {} }) };
let nextCalled = false;
auth.authenticate(req, res, () => { nextCalled = true; });
assert.strictEqual(nextCalled, true);
assert.strictEqual(req.auth.user_id, 'user-1');
assert.deepStrictEqual(req.auth.roles, ['viewer']);
});
it('ska neka expired token', async () => {
const token = jwt.sign({ sub: 'user-1', roles: ['viewer'] }, secret, { algorithm: 'HS256', expiresIn: '-1s' });
const req = { headers: { authorization: `Bearer ${token}` } };
let statusCode = null;
const res = {
status: (code) => { statusCode = code; return { json: () => {} }; }
};
auth.authenticate(req, res, () => {});
assert.strictEqual(statusCode, 401);
});
it('ska neka token utan roller i production', async () => {
// Simulera production-beteende genom att testa logiken direkt
// I production krävs RS256 + publik nyckel, så vi testar att
// auth-modulen kastar fel vid startup om konfig saknas
// Testa att validateAuthConfig kastar fel utan publik nyckel i production
const originalEnv = process.env.NODE_ENV;
const originalPath = process.env.JWT_PUBLIC_KEY_PATH;
process.env.NODE_ENV = 'production';
delete process.env.JWT_PUBLIC_KEY_PATH;
assert.throws(() => auth.validateAuthConfig(), /JWT_PUBLIC_KEY_PATH/);
process.env.NODE_ENV = originalEnv;
if (originalPath) process.env.JWT_PUBLIC_KEY_PATH = originalPath;
});
});
describe('Service-to-service token', () => {
it('ska acceptera giltigt intern token', () => {
const req = { headers: { authorization: `Bearer ${process.env.LEDGER_INTERNAL_TOKEN}` } };
let nextCalled = false;
const res = { status: () => ({ json: () => {} }) };
auth.authenticate(req, res, () => { nextCalled = true; });
assert.strictEqual(nextCalled, true);
assert.strictEqual(req.auth.type, 'service');
assert.deepStrictEqual(req.auth.roles, ['admin']);
});
it('ska neka ogiltigt intern token', () => {
const req = { headers: { authorization: 'Bearer wrong-token' } };
let statusCode = null;
const res = {
status: (code) => { statusCode = code; return { json: () => {} }; }
};
auth.authenticate(req, res, () => {});
assert.strictEqual(statusCode, 401);
});
});
describe('RBAC — requireRole', () => {
it('ska tillåta admin att komma åt admin-resurser', () => {
const req = { auth: { roles: ['admin'] } };
let nextCalled = false;
const res = { status: () => ({ json: () => {} }) };
auth.requireRole('admin')(req, res, () => { nextCalled = true; });
assert.strictEqual(nextCalled, true);
});
it('ska tillåta accountant att komma åt viewer-resurser', () => {
const req = { auth: { roles: ['accountant'] } };
let nextCalled = false;
const res = { status: () => ({ json: () => {} }) };
auth.requireRole('viewer')(req, res, () => { nextCalled = true; });
assert.strictEqual(nextCalled, true);
});
it('ska neka viewer att komma åt accountant-resurser', () => {
const req = { auth: { roles: ['viewer'] } };
let statusCode = null;
const res = {
status: (code) => { statusCode = code; return { json: () => {} }; }
};
auth.requireRole('accountant')(req, res, () => {});
assert.strictEqual(statusCode, 403);
});
});
describe('Tenant-isolering', () => {
it('ska tillåta användare att komma åt sin egen tenant', () => {
const req = {
auth: { type: 'user', tenant_id: 'landvex' },
headers: { 'x-tenant-id': 'landvex' },
body: {}
};
let nextCalled = false;
const res = { status: () => ({ json: () => {} }) };
auth.requireTenantMatch(req, res, () => { nextCalled = true; });
assert.strictEqual(nextCalled, true);
assert.strictEqual(req.tenant_id, 'landvex');
});
it('ska neka användare att komma åt annan tenant', () => {
const req = {
auth: { type: 'user', tenant_id: 'landvex' },
headers: { 'x-tenant-id': 'annan-tenant' },
body: {}
};
let statusCode = null;
const res = {
status: (code) => { statusCode = code; return { json: () => {} }; }
};
auth.requireTenantMatch(req, res, () => {});
assert.strictEqual(statusCode, 403);
});
it('ska tillåta service-konto att komma åt alla tenants', () => {
const req = {
auth: { type: 'service', tenant_id: 'wavult-group' },
headers: { 'x-tenant-id': 'annan-tenant' },
body: {}
};
let nextCalled = false;
const res = { status: () => ({ json: () => {} }) };
auth.requireTenantMatch(req, res, () => { nextCalled = true; });
assert.strictEqual(nextCalled, true);
assert.strictEqual(req.tenant_id, 'annan-tenant');
});
});
console.log('✅ Auth-tester klara');